Подтвердить что ты не робот

Есть ли какое-либо преимущество для определения значения val над def в признаке?

В Scala a val может переопределить def, но a def не может переопределить val.

Итак, есть ли преимущество для объявления признака, например. например:

trait Resource {
  val id: String
}

вместо этого?

trait Resource {
  def id: String
}

Следующий вопрос: как компилятор рассматривает вызов val и def по-другому на практике и какие оптимизации он действительно делает с val s? Компилятор настаивает на том, что val стабильны - что на практике означает для компилятора? Предположим, что подкласс фактически реализует id с помощью val. Есть ли штраф за то, что он указан как def в признаке?

Если мой код не требует стабильности члена id, можно считать, что в этих случаях рекомендуется использовать def в этом случае и переключиться на val только тогда, когда узкое место производительности было идентифицировано здесь - как маловероятно это может быть?

4b9b3361

Ответ 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
  • Разработка кода, который является бинарным упругим

Ответ 2

Разница в основном заключается в том, что вы можете реализовать/переопределить def с val, но не наоборот. Более того, val оцениваются только один раз и def оцениваются каждый раз, когда они используются, используя def в абстрактном определении, дает код, который смешивает признак с большей свободой в отношении того, как обрабатывать и/или оптимизировать реализацию. Поэтому мой смысл в том, чтобы использовать defs всякий раз, когда нет явной веской причины заставить val.

Ответ 3

A val выражение оценивается один раз при объявлении переменной, оно является строгим и неизменным.

A def переоценивается каждый раз, когда вы вызываете его

Ответ 4

def оценивается по имени и val по значению. Это означает более или менее то, что val должно всегда возвращать фактическое значение, а def больше похоже на то, что вы можете получить значение при его оценке. Например, если у вас есть функция

def trace(s: => String ) { if (level == "trace") println s } // note the => in parameter definition

который регистрирует событие только в том случае, если для уровня журнала задана трассировка, и вы хотите зарегистрировать объекты toString. Если вы превысили toString со значением, вам необходимо передать это значение функции trace. Если toString однако def, он будет оцениваться только после того, как он уверен, что уровень журнала - это трассировка, что может сэкономить вам некоторые накладные расходы.   def дает вам большую гибкость, а val потенциально быстрее

Компилятор, traits скомпилированы для интерфейсов java, поэтому при определении члена на trait не имеет значения, если его a var или def. Разница в производительности будет зависеть от того, как вы решите ее реализовать.