2013-12-17 53 views
2

考慮這個簡單的例子:創建(或複製)對象的成本如何?

trait A { def a: String = "a"; def a2: String } 
case class C(b: String, c: String) extends A { 
    val a2 = "a2" 
} 
val c = C("b", "") 
val copy = c.copy(c="c") 

當我與.copy(c="c")更新字段c,被其他字段(aa2b)複製?雖然只有他們的引用被複制,如果我有一個巨大的層次樹,.copy會變得非常昂貴?

同理:

class Foo { 
    val list = List(1,2,3,4,5,6) 
} 
val foo1 = new Foo 
val foo2 = new Foo 

foo1foo2份額的List的實例,或者每次我實例化一個Foo它會創建一個新的Listlistvar而不是val怎麼辦?

回答

2

通常Scala是不可變的,你通常必須自己處理可變的情況。此外,case類本質上是不可變的,它們的複製方法是由編譯器生成的。所以是的,他們會分享相同的對象參考。這是不變性很好的原因之一。

你的第二個問題有點不同。在這種情況下,這些類是在單獨的環境中一個接一個地構建的。

這也是一個好主意,檢查什麼正在編譯:

>scalac -print test.scala 

[[syntax trees at end of cleanup]] // test.scala 
package test { 
    class Foo extends Object { 
     private[this] val list: List = _; 
     <stable> <accessor> def list(): List = Foo.this.list; 
     def <init>(): b.Foo = { 
      Foo.super.<init>(); 
      Foo.this.list = immutable.this.List.apply(scala.this.Predef.wrapIntArray(Array[Int]{1, 2, 3, 4, 5, 6})); 
      () 
     } 
    } 
} 

從這一點我們可以看出斯卡拉每次創建一個新的列表。此更改爲var不會改變任何東西,我們可以檢查:

>scalac -print test.scala 
[[syntax trees at end of cleanup]] // test.scala 
package test { 
    class Foo extends Object { 
     private[this] var list: List = _; 
     <accessor> def list(): List = Foo.this.list; 
     <accessor> def list_=(x$1: List): Unit = Foo.this.list = x$1; 
     def <init>(): b.Foo = { 
      Foo.super.<init>(); 
      Foo.this.list = immutable.this.List.apply(scala.this.Predef.wrapIntArray(Array[Int]{1, 2, 3, 4, 5, 6})); 
      () 
     } 
    } 
} 

它只生成它(def list_=(x$1: List))setter方法。

如果您想引用同一個列表在兩種情況下,然後用對象的默認列表初始化:

object Foo { 
    val DefaultList = List(1,2,3,4,5,6) 
} 

class Foo { 
    var list = Foo.DefaultList 
} 

哪個編譯爲以下幾點:

>scalac -print test.scala 
[[syntax trees at end of cleanup]] // test.scala 
package test { 
    object Foo extends Object { 
     private[this] val DefaultList: List = _; 
     <stable> <accessor> def DefaultList(): List = Foo.this.DefaultList; 
     def <init>(): test.Foo.type = { 
      Foo.super.<init>(); 
      Foo.this.DefaultList = immutable.this.List.apply(scala.this.Predef.wrapIntArray(Array[Int]{1, 2, 3, 4, 5, 6})); 
      () 
     } 
    }; 
    class Foo extends Object { 
     private[this] var list: List = _; 
     <accessor> def list(): List = Foo.this.list; 
     <accessor> def list_=(x$1: List): Unit = Foo.this.list = x$1; 
     def <init>(): test.Foo = { 
      Foo.super.<init>(); 
      Foo.this.list = Foo.DefaultList(); 
      () 
     } 
    } 
} 

正如你所看到的列表只創建一次,然後通過在每個Foo類實例化上分配的參考def DefaultList(): List

+0

感謝您的回答!但我不確定「同一對象參考」的含義。如果案例類有4種混入特性,並且它們的回顯有5個字段,那麼複製時是否必須複製4 * 5 = 20個引用?或者只有4個參考?(更確切地說,當一個類混合在一個特質中,它是否具有來自特質的所有領域,或者僅僅是對特質的虛擬實例的參考?) –

+0

好的,我看到你回答了你的問題: ) –

1

回答我的第一個問題:

具體val S IN的性狀被實現爲Java靜態方法:

// scala 
trait A { def a: String = "a"; } 

// java 
public static java.lang.String a(A); 
Code: 
    0: ldc   #8     // String a 
    2: areturn 

當然,靜態方法不能被複制。所以我不需要擔心巨大的層次結構樹。

抽象val被實現爲硬編碼的常數:

// scala 
trait B { def b: String } 
case class C() { def b = "b" } 

// java 
public java.lang.String b(); 
Code: 
    0: ldc   #47     // String b 
    2: areturn 

它的罰款以及。

當我呼叫.copy()時,唯一會複製的是構造函數參數列表中的字段。