2016-11-04 5 views
3

Как вы думаете, что выдает?Scala recursive val поведение

val foo: String = "foo" + foo 
println(foo) 

val foo2: Int = 3 + foo2 
println(foo2) 

Ответ:

foonull 
3 

Почему? Есть ли какая-то деталь в спецификации, которая описывает/объясняет это?

EDIT: Для того, чтобы прояснить мое удивление, - я понимаю, что foo не определено в val foo: String = "foo" + foo, и именно поэтому он имеет значение по умолчанию null (ноль для целых чисел). Но это не кажется очень «чистым», и я вижу здесь мнения, которые согласны со мной. Я надеялся, что компилятор помешает мне сделать что-то подобное. Это имеет смысл в некоторых частных случаях, например, при определении Stream s, которые являются ленивыми по своей природе, но для строк и целых чисел я ожидал бы либо прекратить меня из-за переназначения в val, либо сказать мне, что я пытаюсь использовать неопределенное значение , точно так же, как если бы я написал val foo = whatever (учитывая, что whatever так и не определился).

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

val bar: String = { 
    val foo: String = "foo" + foo // error: forward reference extends... 
    "bar" 
} 
+0

Какое поведение вы ожидаете? –

+1

Компилятор говорит мне «переназначение в val» или что-то в этом роде. Или сказать мне, что он не знает, что такое 'foo' (поскольку лексический анализ компилятора еще не сохранил этот токен, поскольку он еще не был полностью определен). Определенно не давая ему временное значение «null», которое больше похоже на JavaScript, чем на Scala (с неопределенным, а не нулевым). – slouc

+0

@slouc также замечает разницу в поведении при замене 'val' 'def'. – fxlae

ответ

11

Да, см. SLS Section 4.2.

foo является String, который является ссылочным типом. Он имеет значение по умолчанию null. foo2 - Int, который имеет значение по умолчанию 0.

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

+0

ОК, так что это то же самое, что, например, вы пытаетесь использовать поле внутри некоторого абстрактного класса, значение которого еще не определено в подклассе (из-за порядка линеаризации). Но не должно ли это определение «val» быть захвачено компилятором? Это четкое повторное объявление «val». – slouc

+0

Это не повторное объявление val. Значение val объявляется только один раз, но оно используется до его инициализации. –

+1

Точно, и компилятор мог бы предупредить нас об этом. Это должно быть то же самое, что сказать «val foo = whatever» (учитывая, что 'what' не определен нигде). Но я предполагаю, что происходит «ОК, здесь мы инициализируем val, давайте его создадим и присвоим ему нулевое значение, пока мы не закончим инициализацию». – slouc

1

в Scala Int поддерживается примитивного типа (int) под, и его значение по умолчанию (до инициализации) равен 0.

С другой стороны, String является Object, который действительно null, когда не инициализируется.

1

В обоих случаях foo resp. foo2 имеют значения по умолчанию в соответствии со спецификацией JVM. Это для ссылочного типа и 0 для int или Int - как это делает Скала.

+0

Это на самом деле JMM (модель памяти Java), которая определяет это – Ven

1

К сожалению, scala не так безопасен, как, скажем, Haskell из-за компромиссов с моделью OOP Java («Объектно-ориентированный отвечает функциональному»). Существует безопасное подмножество Scala называется Scalazzi и некоторые из SBT/scalac-плагин могут дать вам больше предупреждений/ошибок:

https://github.com/puffnfresh/wartremover (doesn't check для случая вы нашли хотя)

Возвращаясь обратно к вашему делу , это происходит только для значений, представленных в виде полей и не происходит, когда вы находитесь внутри функции/метода/блока:

scala> val foo2: Int = 3 + foo2 
foo2: Int = 3 

scala> {val foo2: Int = 3 + foo2 } 
<console>:14: error: forward reference extends over definition of value foo2 
     {val foo2: Int = 3 + foo2 } 
          ^

scala> def method = {val a: Int = 3 + a} 
<console>:12: error: forward reference extends over definition of value a 
     def method = {val a: Int = 3 + a} 

причиной такой ситуации является интеграция с Java, как val компилирует в final поле JVM, поэтому Scala сохраняет все инициализации n специфика классов JVM (я считаю, что это часть JSR-133: модель памяти Java).Вот более сложный пример, который объясняет такое поведение:

scala> object Z{ val a = b; val b = 5} 
<console>:12: warning: Reference to uninitialized value b 
    object Z{ val a = b; val b = 5} 
        ^
defined object Z 

scala> Z.a 
res12: Int = 0 

Итак, здесь вы можете увидеть предупреждение, что вы не видели в первую очередь.

+2

Wow, спасибо за указание ситуации внутри блока. – slouc

+0

@slouc добро пожаловать. Я добавил разъяснения о том, почему такое поведение происходит. – dk14

+0

Отлично, спасибо. Я уже принял ответ, прежде чем вы пришли, но я признаю, что это более информативно. – slouc