5 Scala類

簡單類和無參方法

  • 一個簡單的類
class Counter{
  //字段必須初始化
  private var value = 0
  //方法默認公有
  def increment(): Unit ={
    value+=1
  }
  def current()=value
}
//使用類構造對象
val myCounter=new Counter
myCounter.increment()
myCounter.increment()
println(myCounter.current)
  • 調用無參數的方法,可以寫小括號,也可以不寫
  • 如果方法定義時不帶小括號,那麼調用使只能不帶括號
  • 建議:對於改值器方法(改變對象狀態的方法),加上小括號;對於取值器(不會改變對象狀態方法)去掉小括號不錯

帶getter和setter的屬性

  • 一般不使用公有字段,防止一個字段被錯誤修改,使用setter方法設置不能被隨便修改
  • 一對getter/setter方法通常被稱爲屬性。
  • 對於一個公有字段age,scala會自動生成setter和getter方法,其中getter方法爲age,setter方法爲age_=,通過下面的例子可以看到。
  • 對於Person.scala,使用scalac Person.scala編譯,使用javap -private Person查看字節碼;=號被翻譯成了$eq
  • Person.scala如下
class Person{
  var age=0
}
  • 字節碼如下
Compiled from "Person.scala"
public class Person {
  private int age;
  public int age();
  public void age_$eq(int);
  public Person();
}
  • 自己重新定義getter和setter方法,私有的age重新起個名字,方法設置爲age和age_=,通過age_=防止age被減小。
class Person2{
  private var privateAge=0
  def age=privateAge
  def age_=(newAge:Int): Unit ={
    if(newAge>privateAge){
      privateAge=newAge
    }
  }
}
  • 其字節碼如下。可以看到,對於私有的privateAge也生成了私有的getter和setter方法。
Compiled from "Person2.scala"
public class Person2 {
  private int privateAge;
  private int privateAge();
  private void privateAge_$eq(int);
  public int age();
  public void age_$eq(int);
  public Person2();
}
  • 如果字段是私有的,那麼getter和setter方法也是私有的
  • 如果字段是val,則只有getter方法生成
  • 如果不生成任何getter和setter,可以將字段聲明爲private[this]

只帶getter屬性

  • 將字段聲明爲val,只生成一個final修飾的私有字段和一個公有的getter方法。
  • 例如
//Person3.scala
class Person3{
  val name="aa"
}
//查看的字節碼
Compiled from "Person3.scala"
public class Person3 {
  private final java.lang.String name;
  public java.lang.String name();
  public Person3();
  • var foo:自動合成getter和setter方法
  • val foo:自動合成一個getter方法
  • 不能實現只有setter沒有getter
  • 自己定義foo和foo_=

對象私有字段

  • 默認情況下,同一個類的一個對象可以訪問另一個對象的私有字段
class Counter{
  //字段必須初始化
  private var value :Int = 0
  //方法默認公有
  def increment(): Unit ={
    value+=1
  }
  def current():Int=value
  //可以訪問另一個對象的私有字段value
  def isLess(other:Counter):Boolean = { value < other.value }
}
  • 更加嚴格的限制,不能讓它訪問別的對象的私有字段
  • 將字段的定義將private改爲private[this]修飾,即將類私有字段轉換爲對象私有字段
private[this] var value :Int = 0
  • 對於類私有字段,scala會生成私有的getter和setter方法,對於對象私有字段,不會生成getter和setter方法。示例如下,對象私有的card字段沒有生成getter和setter方法。
//Person4.scala
class Person4{
  private var age = 0
  private[this] var card = 0
}
//編譯後
Compiled from "Person4.scala"
public class Person4 {
  private int age;
  private int card;
  private int age();
  private void age_$eq(int);
  public Person4();
}
  • 此外可以用private[類名]定義指定類的方法可以訪問指定的字段。類名必須是當前定義的類,或者包含該類的外部類。
  • private決定了getter或setter是私有或公有,val決定了會不會生成setter方法。所有的字段全部爲私有,getter或setter有私有也有公有。

Bean屬性

  • scala生成的getter和setter方法不是java工具需要的
  • JavaBeans規範把java屬性定義爲一堆getFoo/setFoo方法,只讀的只有getFoo方法。
  • 將scala字段標註爲@BeanProperty
  • 首先導入import scala.beans.BeanProperty
  • 生成四個字段,age、age_=、getAge、setAge
//Person5.scala
import scala.beans.BeanProperty
class Person5{
  @BeanProperty var age = 0
}
//查看
Compiled from "Person5.scala"
public class Person5 {
  private int age;
  public int age();
  public void age_$eq(int);
  public void setAge(int);
  public int getAge();
  public Person5();
}

  • 總結一下
序號 scala字段 生成的方法 何時使用
1 val / var name 公有的name,name_=(對於var) 實現一個公開訪問並背後是字段形式保存的屬性
2 @BeanProperty val / var name 公有的name,name_=(對於var),getName,setName(對於var) 與JavaBeans互操作
3 private val / var name 私有的name,name_=(對於var) 字段的訪問限制在本類的方法
4 private[this] val / var name 不生成 字段訪問限制在同一個對象,不經常用到
5 private[類名] val / var name 依賴於具體實現 字段訪問賦予外部類,不經常用到
  • 各種可能的情況查看一下
//Person6.scala
import scala.beans.BeanProperty
class Person6{
  var name1 = 0
  val name2 = 0
  private var name3 = 0
  private val name4 = 0
  @BeanProperty var name5 = 0
  @BeanProperty val name6 = 0
  private[this] var name7 = 0
  private[this] val name8 = 0
}
//字節碼
Compiled from "Person6.scala"
public class Person6 {
  private int name1;
  private final int name2;
  private int name3;
  private final int name4;
  private int name5;
  private final int name6;
  private int name7;
  private final int name8;
  public int name1();
  public void name1_$eq(int);
  public int name2();
  private int name3();
  private void name3_$eq(int);
  private int name4();
  public int name5();
  public void name5_$eq(int);
  public void setName5(int);
  public int name6();
  public int getName5();
  public int getName6();
  public Person6();
  • 主構造器的字段需要JavaBeans的getter和setter方法
  • class Person(@BeanProperty var name:String)

輔助構造器

  • scala可以有任意多的構造器
  • 最重要的構造器:主構造器
  • 輔助構造器可以有很多
  • 輔助構造器的名稱爲this,(java或C++中構造器的名稱和類名相同,修改類名時不方便)
  • 每一輔助構造器必須以一個對先前定義的其他輔助構造器或主構造器的調用開始
class Person{
  private var name = ""
  private var age = 0
  println("主構造器")
  //第一個輔助構造器
  def this(name:String){
    //調用主構造器
    this()
    println("執行第一輔助構造器")
    this.name=name
  }
  //第二個輔助構造器
  def this(name:String,age:Int){
    //調用第一個輔助構造器
    this(name)
    println("執行第二輔助構造器")
    this.age=age
  }
}

//調用
val p1 = new Person()
println("---")
val p2 = new Person("及時雨宋江")
println("------")
val p3 = new Person("玉麒麟盧俊義",43)
//輸出的結果
主構造器
---
主構造器
執行第一輔助構造器
------
主構造器
執行第一輔助構造器
執行第二輔助構造器

主構造器

  • 如果一個類沒有顯示地定義主構造器,則自動擁有一個無參的主構造器this()
  • 定義主構造器:定義類的時候
  • 主構造器的參數直接放在類名之後
  • 主構造器的參數被編譯成字段,其值被初始化成構造時傳入的參數,如果沒有被聲明爲val或var則只是傳遞參數,不會是字段
  • 如果不帶val或var的參數至少被一個方法使用,將被升格爲字段,其類型是private final,但是不會有getter方法,更不會有setter方法
  • 主構造器會執行類定義的所有語句,參考輔助構造器部分,類定義語句爲代碼塊中除了輔助構造器的所有的都是
  • 主構造器的參數可以是表中的任意形態,例如 private var age:Int。
  • 如果想私有主構造器,可以在參數列表前面加入private關鍵字,這樣就只能通過輔助構造器構造對象class Person private(val name:String,val age:Int)
主構造器參數 生成的字段/方法
name:String 如果有方法使用name,則變爲對象私有字段 ,否則不生成字段。從不生成方法
private val / var name:String 私有字段,私有的getter、setter方法(對於var)
val / var name:String 私有字段,公有的getter、setter方法(對於var)
@BeanProperty val / var name:String 私有字段,公有的Scala版和JavaBeans版getter、setter方法
// Person3.scala
import scala.beans.BeanProperty
class Person3(val name1:Int,
              var name2:Int,
              name3:Int,
              name4:Int,
              private var name5:Int,
              private val name6:Int,
              @BeanProperty var name7:Int,
              @BeanProperty val name8:Int){
  def descrption =  "name3 is " +name3
}

//得到的方法和字段
//所有的字段全部爲私有,getter或setter有私有也有公有。
Compiled from "Person3.scala"
public class Person3 {
  private final int name1;
  private int name2;
  private final int name3;
  private int name5;
  private final int name6;
  private int name7;
  private final int name8;
  public int name1();
  public int name2();
  public void name2_$eq(int);
  private int name5();
  private void name5_$eq(int);
  private int name6();
  public int name7();
  public void name7_$eq(int);
  public void setName7(int);
  public int name8();
  public java.lang.String descrption();
  public int getName7();
  public int getName8();
  public Person3(int, int, int, int, int, int, int, int);

嵌套類

  • 一個外部類的不同對象的內部類是不同的
class Network{
  class Member(val name:String){
    val contacts=new ArrayBuffer[Member]
  }

  private val members=new ArrayBuffer[Member]

  def join(name:String): Member ={
    val m = new Member(name)
    members += m
    m
  }
}

//
val chatter = new Network
val myFace = new Network
val fred = chatter.join("Fred")
val wilma = chatter.join("Wilma")
fred.contacts += wilma
val barney=myFace.join("Barney")
// fred.contacts += barney  //錯誤
  • 如果實現內部類可以相互訪問,使用伴生對象,這樣fred和barney就可以添加好友了。
class Network{
  private val members=new ArrayBuffer[Network.Member]

  def join(name:String): Network.Member ={
    val m = new Network.Member(name)
    members += m
    m
  }
}

object Network{
  class Member(val name:String){
    val contacts=new ArrayBuffer[Member]
  }
}
  • 類型投影的方式,將new ArrayBuffer[Member]改成new ArrayBuffer[Network#Member],其含義是任何Network的Member
  class Member(val name:String){
    val contacts=new ArrayBuffer[Network#Member]
  }
發佈了75 篇原創文章 · 獲贊 83 · 訪問量 7萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章