scala類
- 1.scala類
- 1.1 scala類的方法調用
- 1.2 scala類的apply方法
- 2. scala Object 單例對象
- 2.1 單例對象的apply
- 3. Trait 特質
- 3.1 實作多個特質
- 3.2 new對象時實作特質
- 3.3 函數實作特質
- 3.4 傳名參數
git位址
https://github.com/a18792721831/studyScala.git
1.scala類
scala類和java類定義類似,格式、組成相同。
不同點是scala預設是public,而java預設是package(包内)
首先建立一個scala的類
class MyTest {
var id: Long = _
var name: String = _
private var age: Int = _
def getAge: Int = age
def setAge(age: Int): Unit = this.age = age
override def toString: String = "id:" + this.id +",name:" + this.name + ",age:" + this.age
val gAge = () => println(this.age)
val sAge = (age: Int) => {
this.age = age
age
}
}
這個類很簡單:
兩個共有屬性,一個私有屬性,并且屬性可變,有預設值。
為私有屬性提供了讀取、設定方法。
重寫了toString方法。
還有兩個函數,分别是列印函數和設定函數。
然後建立一個伴生對象
object MyTest{
def main(args: Array[String]): Unit = {
val myTest = new MyTest()
println(myTest)
myTest.id = 1
myTest.name = "lili"
myTest.age = 234
println(myTest)
println(myTest.getAge)
myTest.setAge(0)
println(myTest)
println()
println(myTest.gAge)
println(myTest.sAge)
myTest.gAge
println(myTest.sAge(3))
println(myTest.getAge)
}
}
先别問為什麼需要建立伴生對象,後面會研究到的。
先執行看看結果
id:0,name:null,age:0
id:1,name:lili,age:234
234
id:1,name:lili,age:0
com.study.MyTest$$Lambda$1/2093631819@1ff8b8f
com.study.MyTest$$Lambda$2/1828972342@387c703b
3
3
Process finished with exit code 0
可以看到,scala類的屬性、方法和java基本一樣。
scala的函數與java的lambda是一個。
接下來我們看下反編譯java
有點意思,好像與我們的class和object能對應上。
這兩個檔案,用class檢視軟體隻能看伴生對象的class檔案
接下來用線上反編譯等手段,看下scala類
import java.lang.invoke.SerializedLambda;
import scala.runtime.BoxesRunTime;
import scala.Predef$;
import scala.Function1;
import scala.runtime.BoxedUnit;
import scala.Function0;
import scala.reflect.ScalaSignature;
public class MyTest
{
private long id;
private String name;
private int com$study$MyTest$$age;
private final Function0<BoxedUnit> gAge;
private final Function1<Object, Object> sAge;
public static void main(final String[] args) {
MyTest$.MODULE$.main(args);
}
public long id() {
return this.id;
}
public void id_$eq(final long x$1) {
this.id = x$1;
}
public String name() {
return this.name;
}
public void name_$eq(final String x$1) {
this.name = x$1;
}
private int com$study$MyTest$$age() {
return this.com$study$MyTest$$age;
}
public void com$study$MyTest$$age_$eq(final int x$1) {
this.com$study$MyTest$$age = x$1;
}
public int getAge() {
return this.com$study$MyTest$$age();
}
public void setAge(final int age) {
this.com$study$MyTest$$age_$eq(age);
}
@Override
public String toString() {
return new StringBuilder(14).append("id:").append(this.id()).append(",name:").append(this.name()).append(",age:").append(this.com$study$MyTest$$age()).toString();
}
public Function0<BoxedUnit> gAge() {
return this.gAge;
}
public Function1<Object, Object> sAge() {
return this.sAge;
}
public MyTest() {
this.gAge = (Function0<BoxedUnit>)(() -> Predef$.MODULE$.println((Object)BoxesRunTime.boxToInteger($this.com$study$MyTest$$age())));
this.sAge = (Function1<Object, Object>)(age -> {
$this.com$study$MyTest$$age_$eq(age);
return age;
});
}
}
可以看到,屬性都被編譯為了私有,同時生成了一些用到的方法
根據名字猜測:
這個main就是java中我們經常寫的main
被代理到了内部類中了。内部類就是我們的伴生對象。
應該是提供直接通路的。
在Java中,我們的屬性如果是公有或者保護或者預設,那麼在同路徑的類中是可以直接使用的。
在scala中預設是公有的。
是以可以直接用。
但是,實際上還是私有的,隻不過生成了屬性同名的通路方法。造成了我們對于私有屬性可以直接使用的假像(對于無參方法,會自動填充小括弧)
應該是為了實作公有屬性直接指派。
toString方法用StringBuilder進行了優化。
函數是在構造時進行初始化的。
綜上,原本期望使用
_
進行初始化屬性,結果反編譯沒有展現出來。。。
1.1 scala類的方法調用
之前就知道,在scala中,如果沒有參數,可以将方法的小括弧省略。
但是,還有一句,如果在定義時,方法就沒有小括弧,那麼在使用時,也不能加小括弧。
比如:
class MyApply {
def apply() = println("apply class with ()")
def apply1 = println("apply1 class without ()")
def apply2() = println("apply2 class without ()")
}
我們建立了一個scala類,scala類有三個方法
apply()
apply1
apply2()
然後我們建立一個object去調用
object MyTestApply {
def main(args: Array[String]): Unit = {
val myApply = new MyApply
println(myApply)
myApply()
myApply.apply1
myApply.apply2
myApply.apply2()
}
}
object也非常的簡單,首先建立了一個不可變對象,然後列印了對象,然後分别調用這三個方法。
輸出如下:
com.study.MyApply@2be94b0f
apply class with ()
apply1 class without ()
apply2 class without ()
apply2 class without ()
Process finished with exit code 0
接下來看看Java代碼
這是scala類的
這是object對應的類的
這是object對應的内部類
1.2 scala類的apply方法
在調用scala類的apply方法時,可以不顯示的調用,而是使用變量名加括弧調用。
注意,這樣方式隻适用于有小括弧的apply方法。
2. scala Object 單例對象
單例對象用于持有一個類的唯一執行個體。
為什麼?
看看1中的例子。
我們建立了一個scala類,一個單例對象。
反編譯結果是兩個類,一個内部類。
首先scala類對應了反編譯Java類,而單例對象生成了反編譯Java類和内部類。
單例對象的反編譯Java類作為執行入口,将實際的操作轉到内部類的main方法
在内部類中定義了靜态公有不可繼承的MODULE$對象。反編譯Java類通過這個對象調用實際的内部類的main方法。
是以,可以這麼認為,因為内部類中的MODULE$對象是實際操作入口,也是靜态的,直接new的。是以object内部類持有了object的唯一入口,也即唯一執行個體。
MD,有點繞。後面慢慢了解吧。
反正就這樣用。
2.1 單例對象的apply
在單例對象中也可以實作scala類中apply方法,直接讓變量+()調用apply方法。
在單例對象中也可以實作。不過,因為單例對象是單例的,是以可以直接Object名字+()調用單例對象的apply
舉例如下:
首先建立一個object:
object TestApply {
def apply() = println("TestApply")
}
調用
object TestMain{
def main(args: Array[String]): Unit = {
TestApply()
}
}
運作結果
TestApply
Process finished with exit code 0
看下反編譯結果
3. Trait 特質
怎麼說呢,這玩意和Java接口應該是一個東西(jdk1.8之後的接口,可以寫預設方法)
建立一個特質
class實作
class繼承
object實作
内部匿名實作
測試
是以說,Java中怎麼用接口,在scala中就怎麼用trait
不過,scala貌似不支援上下轉型。
3.1 實作多個特質
我們在上面的基礎上在建立一個特質
然後建立scala類實作
建立object調用
執行結果
這玩意和接口完全相同。
看下反編譯代碼
但是多多少少還是和沒有實作特質的scala類有一定的差別。
沒有實作特質的scala類的反編譯代碼實際所有的操作都在内部類(應該不叫内部類…不過都是用$進行區分,一個在錢,一個在後),但是實作了特質的scala類的實作都在外部類
3.2 new對象時實作特質
scala比較騷的一點是可以在new對象時實作特質。
Java貌似不行。
這就使得scala非常的靈活。
舉個例子:
首先建立一個普通的類
在new對象時,實作特質
看看反編譯代碼
這個應該是繼承原來的scala類,實作了特質的一個内部類
表現為new後面實作了方法(内部類)
還有就是類名前面有$辨別等等。
3.3 函數實作特質
在scala中,主要是對象的函數式程式設計。
在某些資料中,函數是這樣被定義的:
函數是一些特質實作的集合。
是以函數的本質是類的執行個體。
舉個例子
首先看看函數的特質
接下來實作它
然後進行使用
輸出
當然,還有快捷的寫法
使用
=>
文法糖
看下反編譯代碼
3.4 傳名參數
傳名參數是指類似如下的定義
表示方法test的參數是一個函數,=>文法糖,而函數的執行結果是String,然後test對函數的執行結果的String進行操作。舉例:
這裡存在将方法自動展開,即EAT展開。
如果在需要函數的地方傳入了方法,自動将方法轉為函數。