考虑这个简单的例子:
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")
当我使用c
更新字段.copy(c="c")
时,是否复制了其他字段(a
,a2
和b
)?事件虽然只复制了他们的引用,如果我有一个庞大的层次结构树,.copy
会变得非常昂贵吗?
类似地:
class Foo {
val list = List(1,2,3,4,5,6)
}
val foo1 = new Foo
val foo2 = new Foo
foo1
和foo2
是否共享List
的实例,或每当我实例化Foo
时,它会创建新的List
?如果list
是var
而不是val
,该怎么办?
答案 0 :(得分:2)
通常Scala是不可变的,您通常必须自己处理可变案例。案例类本质上也是不可变的,它们的复制方法是由编译器生成的。所以是的,他们将共享相同的对象引用。这是不变性很好的原因之一。
你的第二个问题有点不同。在这种情况下,这些类在单独的上下文中一个接一个地构建。
检查正在编译的内容也是一个好主意:
>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}));
()
}
}
}
从这里我们可以看到Scala每次创建一个新列表。将此更改为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}));
()
}
}
}
它只为它生成了一个setter方法(def list_=(x$1: List)
)。
如果您想在两种情况下引用相同的列表,请使用对象的默认列表对其进行初始化:
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
进行引用。
答案 1 :(得分:1)
回答我自己的第一个问题:
特征中的具体val
实现为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()
时,将复制的唯一内容是构造函数参数列表中的字段。