Вопрос по scala, final, field – Почему `val` внутри` object` не является финальным автоматически?

32

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

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 само по себе окончательно, а значит и его участники?

Согласовано. Я относительно новичок в Скале ... пытался исследовать возможные пути, о которых вы не думали. Tony K.
Вам следует изменить принятый ответ на @ extempore (Пол Филлипс). В этом случае,final заставляет значение быть встроенным во время компиляции. drstevens
@TonyK. - что вы имеете в виду? Даже если бы яtrait T { def a: Int }объект переопределитa в правилах линеаризации. Если бы у меня былоtrait T { def a: Int = 33 }хорошо в этом случаеoverride final val это невозможно. Но я думаю, что это все еще не дисквалифицирует подходnon-overriding vals final по умолчанию. 0__
Гектометр как насчет черт, которые могут быть использованы? Tony K.

Ваш Ответ

3   ответа
21

Вы «не спрашиваете», почему «они не являются окончательными», вы «спрашиваете», почему они «не встроены». Просто так получается, что 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       

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

финал также не означает константу. Попробуйте & quot; final var x = 1; х = 2 ".
похоже, это должен быть принятый ответ
24

Это решается в явном виде вспецификацияи они автоматически становятся окончательными:

Members of final classes or objects are implicitly also final, so the final modifier is generally redundant for them, too. Note, however, that constant value definitions (§4.1) do require an explicit final modifier, even if they are defined in a final class or object.

Ваш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();
}

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

Поэтому я оставлю свой ответ, но он не окончательный.

Спасибо, что спецификация довольно ясна, говоряvals нужно явноfinal, Странно, что 2.10.0-M7, возможно, стоит проверить полученный код соответствия шаблона (возможно,@switch молча уронили?) 0__
Я думал, что вы можете включить только постоянные значения, в этом случае 2.10-M7 снял требованиеfinal что ты цитировал. Нет?
@RexKerr: Действительно. Я не помню, чтобы видел это в любом из журналов изменений, но я проверю снова.
Да, я читал это так, хотя яdo disagree со спецификацией здесь. Как я уже сказал, я думаю, что это ненужный шум. 0__
@ 0__: Вы читаете, что требуется модификатор? Сейчас я просто смущен этим, но, по крайней мере, я бы сказал, что если бы у нас не было возможности определить постоянное значение (например, мы получили"0".toInt или жеnew A на правой стороне), тоfinal избыточно
3

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

A constant value definition is of the form final val x = e where e is a constant expression (§6.24). The final modifier must be present and no type annotation may be given. References to the constant value x are themselves treated as constant expressions; in the generated code they are replaced by the definition’s right-hand side e.

По значимости:

  • No type annotation may be given
  • The expression e is used in the generated code (by my reading, as the original unevaluated constant expression)

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

Я думаю, что это особенно интересно, что аннотации типов не могут быть даны.

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

Похожие вопросы