天天看點

scala類-單例對象-特質

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

scala類-單例對象-特質

有點意思,好像與我們的class和object能對應上。

這兩個檔案,用class檢視軟體隻能看伴生對象的class檔案

scala類-單例對象-特質

接下來用線上反編譯等手段,看下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;
        });
    }
}      

可以看到,屬性都被編譯為了私有,同時生成了一些用到的方法

根據名字猜測:

scala類-單例對象-特質

這個main就是java中我們經常寫的main

被代理到了内部類中了。内部類就是我們的伴生對象。

scala類-單例對象-特質

應該是提供直接通路的。

在Java中,我們的屬性如果是公有或者保護或者預設,那麼在同路徑的類中是可以直接使用的。

在scala中預設是公有的。

是以可以直接用。

但是,實際上還是私有的,隻不過生成了屬性同名的通路方法。造成了我們對于私有屬性可以直接使用的假像(對于無參方法,會自動填充小括弧)

scala類-單例對象-特質

應該是為了實作公有屬性直接指派。

scala類-單例對象-特質

toString方法用StringBuilder進行了優化。

scala類-單例對象-特質

函數是在構造時進行初始化的。

綜上,原本期望使用​

​_​

​進行初始化屬性,結果反編譯沒有展現出來。。。

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類的

scala類-單例對象-特質

這是object對應的類的

scala類-單例對象-特質

這是object對應的内部類

scala類-單例對象-特質

1.2 scala類的apply方法

在調用scala類的apply方法時,可以不顯示的調用,而是使用變量名加括弧調用。

注意,這樣方式隻适用于有小括弧的apply方法。

2. scala Object 單例對象

單例對象用于持有一個類的唯一執行個體。

為什麼?

看看1中的例子。

我們建立了一個scala類,一個單例對象。

反編譯結果是兩個類,一個内部類。

首先scala類對應了反編譯Java類,而單例對象生成了反編譯Java類和内部類。

單例對象的反編譯Java類作為執行入口,将實際的操作轉到内部類的main方法

scala類-單例對象-特質
scala類-單例對象-特質

在内部類中定義了靜态公有不可繼承的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      

看下反編譯結果

scala類-單例對象-特質
scala類-單例對象-特質
scala類-單例對象-特質
scala類-單例對象-特質
scala類-單例對象-特質

3. Trait 特質

怎麼說呢,這玩意和Java接口應該是一個東西(jdk1.8之後的接口,可以寫預設方法)

建立一個特質

scala類-單例對象-特質

class實作

scala類-單例對象-特質

class繼承

scala類-單例對象-特質

object實作

scala類-單例對象-特質

内部匿名實作

scala類-單例對象-特質

測試

scala類-單例對象-特質

是以說,Java中怎麼用接口,在scala中就怎麼用trait

不過,scala貌似不支援上下轉型。

3.1 實作多個特質

我們在上面的基礎上在建立一個特質

scala類-單例對象-特質

然後建立scala類實作

scala類-單例對象-特質

建立object調用

scala類-單例對象-特質

執行結果

scala類-單例對象-特質

這玩意和接口完全相同。

看下反編譯代碼

scala類-單例對象-特質

但是多多少少還是和沒有實作特質的scala類有一定的差別。

沒有實作特質的scala類的反編譯代碼實際所有的操作都在内部類(應該不叫内部類…不過都是用$進行區分,一個在錢,一個在後),但是實作了特質的scala類的實作都在外部類

3.2 new對象時實作特質

scala比較騷的一點是可以在new對象時實作特質。

Java貌似不行。

這就使得scala非常的靈活。

舉個例子:

首先建立一個普通的類

scala類-單例對象-特質

在new對象時,實作特質

scala類-單例對象-特質
scala類-單例對象-特質

看看反編譯代碼

scala類-單例對象-特質

這個應該是繼承原來的scala類,實作了特質的一個内部類

scala類-單例對象-特質

表現為new後面實作了方法(内部類)

還有就是類名前面有$辨別等等。

3.3 函數實作特質

在scala中,主要是對象的函數式程式設計。

在某些資料中,函數是這樣被定義的:

函數是一些特質實作的集合。

是以函數的本質是類的執行個體。

舉個例子

首先看看函數的特質

scala類-單例對象-特質

接下來實作它

scala類-單例對象-特質

然後進行使用

scala類-單例對象-特質

輸出

scala類-單例對象-特質
scala類-單例對象-特質

當然,還有快捷的寫法

scala類-單例對象-特質

使用 ​

​=>​

​文法糖

看下反編譯代碼

scala類-單例對象-特質

3.4 傳名參數

傳名參數是指類似如下的定義

scala類-單例對象-特質

表示方法test的參數是一個函數,=>文法糖,而函數的執行結果是String,然後test對函數的執行結果的String進行操作。舉例:

scala類-單例對象-特質

這裡存在将方法自動展開,即EAT展開。

如果在需要函數的地方傳入了方法,自動将方法轉為函數。

scala類-單例對象-特質