Ответ 1
Короткий ответ:
Насколько я могу судить, значения всегда доступны через метод доступа. Использование def
определяет простой метод, который возвращает значение. Использование val
определяет конечное поле private [*] с использованием метода доступа. Таким образом, с точки зрения доступа между ними существует очень мало различий. Разница концептуальна, def
пересматривается каждый раз, а val
оценивается только один раз. Это, очевидно, может повлиять на производительность.
[*] Java private
Длинный ответ:
Возьмем следующий пример:
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()
не использует поле id
, но использует метод доступа.
И, наконец, если мы не переопределим, тогда мы получим метод, который вызывает статический метод в вспомогательном классе признаков:
public class ResourceNoneClass implements ResourceDef, ScalaObject {
public volatile String id() {
return ResourceDef$class.id(this);
}
}
Я вырезал конструкторы в этих примерах.
Итак, всегда используется метод доступа. Я предполагаю, что это во избежание осложнений при расширении нескольких признаков, которые могут реализовать те же методы. Это очень быстро усложняется.
Еще более длинный ответ:
Джош Суэрет сделал очень интересный разговор о Binary Resilience в Scala Days 2012, который освещает предысторию этого вопроса. Резюме для этого:
В этой статье основное внимание уделяется бинарной совместимости на JVM, и что это означает быть бинарным. Схема махинаций бинарных несовместимость в Scala подробно описывается с последующим набором правил и рекомендаций, которые помогут разработчикам обеспечить их собственные релизы библиотеки являются бинарными и бинарными.
В частности, этот разговор выглядит следующим образом:
- Черты и двоичная совместимость
- Сериализация Java и анонимные классы
- Скрытые творения ленивых vals
- Разработка кода, который является бинарным упругим