case class 和class的區別以及構造器參數辨析 case class 和class的區別以及構造器參數辨析

工做中偶然發現Scala構造方法中的參數,不管是否有val/var修飾均可以順利編譯運行,以下:html

1     class AA(name: String)
2     class BB(val name: String)

那麼二者的區別在哪裏呢?對於case class呢?其區別又在哪裏?其應用場景又在哪裏呢?下面就辨析一下以下幾個類的區別java

 1     class AA(name: String)
 2     class BB(val name: String)
 3     class CC(var name: String)
4 class DD(private val name: String) 5 class EE(private[this] val name: String)
6 case class FF(name: String) 7 case class GG(val name: String) 8 case class HH(var name: String) 9 case class II(private val name: String) 10 case class JJ(private[this] val name: String)

 

 單純的從代碼中來看,發現不了什麼區別,只是簡單的多了一個val的修飾符。爲了一探究竟,先對源碼進行編譯,而後經過javap對其class文件進行反編譯,查看其與源碼的區別。app

1、普通類構造器中val/var 存在和不存在的區別函數

源碼:post

1     class AA(name: String)
2     class BB(val name: String)
3     class CC(var name: String)

 反編譯結果:this

 1 Compiled from "Test.scala"
 2 public class AA {
 3   public AA(java.lang.String);
 4 }
 5 
 6 Compiled from "Test.scala"
 7 public class BB {
 8   private final java.lang.String name;  9   public java.lang.String name(); 10   public BB(java.lang.String);
11 }
12 
13 Compiled from "Test.scala"
14 public class CC {
15   private java.lang.String name; 16   public java.lang.String name(); 17   public void name_$eq(java.lang.String); 18   public CC(java.lang.String);
19 }

  結論:構造器中val修飾的參數,編譯以後會在該類中添加一個private final全局常量,同時提供一個用於獲取該常量的public方法,無設置方法。url

   構造器中var修飾的參數,編譯以後會在該類中添加一個private全局變量,同時提供兩個獲取和設置該變量值的public方法。spa

    構造器中無修飾符的參數,則該參數屬於構造函數內的局部參數,僅僅在該構造函數內部訪問scala

2、普通類構造器中private和private[this] 修飾參數的區別3d

源碼:

1     class DD(private val name: String)
2     class EE(private[this] val name: String)

 反編譯結果:

Compiled from "Test.scala"
public class DD {
  private final java.lang.String name; private java.lang.String name(); public DD(java.lang.String);
}

Compiled from "Test.scala"
public class EE {
  public EE(java.lang.String);
}

 結論:private 修飾的構造器參數,編譯以後會在該類中添加一個private final的全局常量,同時提供一個private的訪問該參數的方法。即該參數可在該類範圍內訪問

   private[this]修飾的構造器參數,不存在全局變量,只能在該構造方法中訪問,在該類中沒法訪問。同 class AA(name: String)

注意:Scala整個類體就是其構造函數,因此,站在Scala角度看,private[this]修飾的構造器參數可以在整個類中訪問,而站在Java角度看,該參數僅僅可以在構造函數中訪問,在類中沒法訪問。而站在Scala角度看,private[this]和 private的主要區別在於,private[this]修飾的參數沒法經過e.name的方式訪問,即便在該類的內部。注意下圖:

圖中,在EE#show中是沒法訪問that.name的,即便that的類型自己就是EE也不行的。這纔是private[this]和private在Scala中的主要區別。

 3、普通class和case class的區別

源碼:

1   case class FF(name: String)

 編譯以後會發如今文件夾下面多出兩個class文件,一個爲FF.class,另外一個爲FF$.class文件。對兩個文件進行反編譯

反編譯結果:

 1 Compiled from "Test.scala"
 2 public class FF implements scala.Product,scala.Serializable {
 3     //private final 修飾的name常量
 4     private final java.lang.String name;  5     //public修飾獲取name的方法,可用於外部訪問
 6     public java.lang.String name();  7     //public修飾的構造函數
 8     public FF(java.lang.String);  9     public static scala.Option<java.lang.String> unapply(FF);
10     public static FF apply(java.lang.String);
11     public static <A> scala.Function1<java.lang.String, A> andThen(scala.Function1<FF, A>);
12     public static <A> scala.Function1<A, FF> compose(scala.Function1<A, java.lang.String>);
13     public FF copy(java.lang.String);
14     public java.lang.String copy$default$1();
15     public java.lang.String productPrefix();
16     public int productArity();
17     public java.lang.Object productElement(int);
18     public scala.collection.Iterator<java.lang.Object> productIterator();
19     public boolean canEqual(java.lang.Object);
20     public int hashCode();
21     public java.lang.String toString();
22     public boolean equals(java.lang.Object);
23 }
24 
25 Compiled from "Test.scala"
26 public final class FF$ extends scala.runtime.AbstractFunction1<java.lang.String, FF> implements scala.Serializable {
27     //靜態的FF$對象
28     public static FF$ MODULE$; 29     //構造函數爲private
30     private FF$(); 31     
32     //返回FF對象的 apply方法
33     public FF apply(java.lang.String); 34     
35     public static {};
36     public final java.lang.String toString();
37     public scala.Option<java.lang.String> unapply(FF);
38     private java.lang.Object readResolve();
39     public java.lang.Object apply(java.lang.Object);
40 }

  分析:

  先看FF.class

   一、對比class AA(name: String)的結果來看,case class 自動實現了scala.Product,scala.Serializable兩個特質(接口),所以,case class類中天然也會實現這兩個特質中的抽象方法/覆寫一些方法(11~22行)。

1 public class AA
2 public class FF implements scala.Product,scala.Serializable 

    二、對比 public class BB,在類內部都具備一個全局常量name和一個獲取該常量的public方法,同時二者都具有一個public的構造函數。

   三、實現了一些特質中的一些方法,覆寫了Java Object中的一些方法。例如:andThen() toString() hashCode()等

   再看FF$

    一、有一個public  static修飾名爲 MODULE$ 的 FF$對象

    二、構造器被私有化,用private修飾。

    三、組合一、 二、兩點可知,FF$是一個單例類。其矢志不渝就是Scala中FF類的伴生對象。 

 結論

  Scala編譯器在對case class進行編譯的時候作了特殊處理,擴展了其方法和功能,加入了scala.Product,scala.Serializable的特性,同時爲其提供了該類的伴生對象。另外,對於case class 構造器參數,其默認以public修飾,可容許外部調用

 4、其餘

 上面幾個對比理解了,下面這幾個類之間的區別也就能夠觸類旁通了。

1     case class GG(val name: String)
2     case class HH(var name: String)
3     case class II(private val name: String)
4     case class JJ(private[this] val name: String)

 

 

 

=========================================

原文連接:case class 和class的區別以及構造器參數辨析 轉載請註明出處!

=========================================

-----end

相關文章
相關標籤/搜索