简短的回答:
据我所知,这些值总是通过访问器方法访问的。Usingdef
定义了一个简单的方法,它返回值。Usingval
定义了一个私有的 [*] final 字段,带有一个访问器方法。所以在访问方面,两者的区别非常小。差异是概念性的,def
每次都会重新评估,并且val
只评估一次。这显然会对性能产生影响。
[*] Java 私有
长答案:
让我们看下面的例子:
trait ResourceDef {
def id: String = "5"
}
trait ResourceVal {
val id: String = "5"
}
ResourceDef
&产生相同的ResourceVal
代码,忽略初始化器:
public interface ResourceVal extends ScalaObject {
volatile void foo$ResourceVal$_setter_$id_$eq(String s);
String id();
}
public interface ResourceDef extends ScalaObject {
String id();
}
对于产生的子类(包含方法的实现),ResourceDef
产生的结果与您期望的一样,注意该方法是静态的:
public abstract class ResourceDef$class {
public static String id(ResourceDef $this) {
return "5";
}
public static void $init$(ResourceDef resourcedef) {}
}
对于 val,我们只需调用包含类中的初始化程序
public abstract class ResourceVal$class {
public static void $init$(ResourceVal $this) {
$this.foo$ResourceVal$_setter_$id_$eq("5");
}
}
当我们开始扩展时:
class ResourceDefClass extends ResourceDef {
override def id: String = "6"
}
class ResourceValClass extends ResourceVal {
override val id: String = "6"
def foobar() = id
}
class ResourceNoneClass extends ResourceDef
在我们覆盖的地方,我们在类中获得了一个方法,它可以满足您的期望。def 是简单的方法:
public class ResourceDefClass implements ResourceDef, ScalaObject {
public String id() {
return "6";
}
}
并且 val 定义了一个私有字段和访问器方法:
public class ResourceValClass implements ResourceVal, ScalaObject {
public String id() {
return id;
}
private final String id = "6";
public String foobar() {
return id();
}
}
请注意,甚至foobar()
不使用 field id
,而是使用访问器方法。
最后,如果我们不重写,那么我们会得到一个调用 trait 辅助类中的静态方法的方法:
public class ResourceNoneClass implements ResourceDef, ScalaObject {
public volatile String id() {
return ResourceDef$class.id(this);
}
}
我已经删除了这些示例中的构造函数。
因此,总是使用访问器方法。我认为这是为了避免在扩展可以实现相同方法的多个特征时出现并发症。它很快变得复杂起来。
更长的答案:
Josh Suereth在 Scala Days 2012上做了一个非常有趣的关于二元弹性的演讲,涵盖了这个问题的背景。对此的摘要是:
本次演讲的重点是 JVM 上的二进制兼容性以及二进制兼容的含义。深入描述了 Scala 中二进制不兼容机制的概要
,然后是一组规则和指南,这些规则和指南将帮助开发人员确保他们自己的库版本既兼容二进制又具有二进制弹性。
特别是,本次演讲着眼于:
- 特征和二进制兼容性
- Java 序列化和匿名类
- 懒惰的 vals 的隐藏创作
- 开发具有二进制弹性的代码