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

Почему `val` внутри` объекта` не является автоматически окончательным?

В чем причина того, что val not (?) является автоматически окончательным в одиночных объектах? Например.

object NonFinal {
   val a = 0
   val b = 1

   def test(i: Int) = (i: @annotation.switch) match {
      case `a` => true
      case `b` => false
   }
}

приводит к:

<console>:12: error: could not emit switch for @switch annotated match
          def test(i: Int) = (i: @annotation.switch) match {
                                                     ^

В то время как

object Final {
   final val a = 0
   final val b = 1

   def test(i: Int) = (i: @annotation.switch) match {
      case `a` => true
      case `b` => false
   }
}

Скомпилирует без предупреждений, поэтому предположительно генерирует более быструю таблицу сопоставления шаблонов.

Мне нужно добавить final для меня чистый раздражающий шум. Разве это не финал object и, следовательно, его члены?

4b9b3361

Ответ 1

Это явно указано в спецификации, и они автоматически окончательны:

Члены конечных классов или объектов неявно также являются окончательными, поэтому модификатор final обычно является избыточным для них. Обратите внимание, однако, что определения константы (§4.1) требуют явного модификатора final, даже если они определены в конечном классе или объекте.

Ваш пример final без компиляции без ошибок (или предупреждений) с 2.10-M7, поэтому я бы предположил, что существует проблема с проверкой @switch в более ранних версиях и что участники фактически являются окончательными.


Обновление: на самом деле это более любопытно, чем я ожидал, - если мы скомпилируем следующее: либо 2.9.2, либо 2.10-M7:

object NonFinal {
  val a = 0
}

object Final {
  final val a = 0
}

javap показывает разницу:

public final class NonFinal$ implements scala.ScalaObject {
  public static final NonFinal$ MODULE$;
  public static {};
  public int a();
}

public final class Final$ implements scala.ScalaObject {
  public static final Final$ MODULE$;
  public static {};
  public final int a();
}

Вы видите то же самое, даже если правая часть определения значений не является постоянным выражением.

Итак, я оставлю свой ответ, но это не окончательно.

Ответ 2

Вы не спрашиваете: "Почему они не окончательные", вы спрашиваете: "Почему они не включены". Просто случается, что final - это то, как вы комментируете компилятор, что вы хотите, чтобы они были встроены.

Причина, по которой они не являются автоматически встроенными, - это отдельная компиляция.

object A { final val x = 55 }
object B { def f = A.x }

Когда вы скомпилируете это, B.f возвращает 55, буквально:

public int f();
  0: bipush        55
  2: ireturn       

Это означает, что если вы перекомпилируете A, B не обратит внимания на это изменение. Если x не отмечен окончательным в A, тогда B.f выглядит следующим образом:

  0: getstatic     #19                 // Field A$.MODULE$:LA$;
  3: invokevirtual #22                 // Method A$.x:()I
  6: ireturn       

Кроме того, чтобы исправить один из других ответов, final не означает неизменяемость в scala.

Ответ 3

Чтобы решить центральный вопрос об окончательном объекте, я думаю, что этот раздел из спецификации более уместен:

Определение постоянного значения имеет вид final val x = e где е - постоянное выражение (§6.24). Последний модификатор должен присутствовать, и аннотация типа типа не может быть задана. Ссылки на постоянное значение x сами рассматриваются как постоянные выражения; в сгенерированном коде они заменяются определениями правой стороны e.

Значимость:

  • Аннотации типа не могут быть указаны
  • Выражение e используется в сгенерированном коде (моим чтением, как исходным неоценимым постоянным выражением)

Мне кажется, что компилятор требуется спецификацией, чтобы использовать эти более похожие замены макросов, а не значения, которые оцениваются на месте во время компиляции, что может повлиять на результат выполнения кода.

Я думаю, что особенно интересно, что аннотация не может быть задана.

Это, я думаю, указывает на наш окончательный ответ, хотя я не могу придумать пример, показывающий разницу во времени для этих требований. Фактически, в моем интерпретаторе 2.9.2 я даже не получаю принудительное выполнение первого правила.