Ответ 1
Вы можете сделать:
val whatever =
if (someCondition)
"final value"
else
"other value"
Почему это невозможно:
def main(args:Array[String]) {
val whatever:String // Have it uninitialized here
if(someCondition) {
whatever = "final value" // Initialize it here
}
}
Я не понимаю, почему это не должно быть законным. Я знаю, что могу сделать это var
, но почему мы должны инициализировать val
именно тогда, когда мы объявляем его? Не кажется ли более логичным возможность инициализировать его позже?
Вы можете сделать:
val whatever =
if (someCondition)
"final value"
else
"other value"
Решение Java на самом деле является обходным решением проблемы, что не все выражения возвращают значения, поэтому вы не можете записать это в Java:
final String whatever = if (someCondition) {
"final value"
} else {
"other value"
}
Все чаще тренд в Java заключается в использовании тернарного оператора:
final String whatever = someCondition ? "final value" : "other value"
Что подходит для этого ограниченного варианта использования, но полностью несостоятельно, когда вы начинаете работать с операторами switch и несколькими конструкторами.
Scala подход здесь различен. Вся конструкция объекта должна, в конечном счете, проходить через один "первичный" конструктор, все выражения возвращают значение (даже если оно Unit
, эквивалентное Java Void
), и инъекция конструктора сильно предпочтительна. Это приводит к тому, что графы объектов строятся как прямой ациклический график, а также отлично работают с неизменяемыми объектами.
Вы также должны знать, что объявление и определение переменных в отдельных операциях является, как правило, плохой практикой при работе с несколькими потоками, и может оставить вас уязвимыми для выявления нулей и условий гонки, когда вы их меньше всего ожидаете (хотя это на самом деле не проблема при построении объекта). Атомное создание неизменных значений - это всего лишь один из аспектов того, как функциональные языки помогают справляться с concurrency.
Это также означает, что компилятор Scala может избежать некоторых ужасно сложных анализов потока из спецификации языка Java.
Как было сказано ранее, Scala Way ™:
val whatever =
if (someCondition)
"final value"
else
"other value"
Подход, который также масштабируется до других структур управления:
val whatever = someCondition match {
case 1 => "one"
case 2 => "two"
case 3 => "three"
case _ => "other"
}
С небольшим количеством опыта Scala вы обнаружите, что этот стиль помогает компилятору помочь вам, и вы должны найти себе программы с меньшим количеством ошибок!
Используйте lazy val
следующим образом:
def main(args:Array[String]) {
lazy val whatever:String = if (someCondition) "final value" else "other value"
// ...
println(whatever) // will only initialize on first access
}
В дополнение к тому, что говорили другие, обратите внимание, что Java разрешает "пустые конечные" "переменные", что является особенностью, которую я пропустил:
final Boolean isItChristmasYet;
if (new Date().before(christmas)) {
isItChristmasYet = Boolean.FALSE;
} else {
isItChristmasYet = Boolean.TRUE;
}
Однако, благодаря анализу потока данных в компиляторе, javac не позволит вам оставить переменную whatever
не назначенной, если someCondition
не выполняется.
Поскольку цель 'val' - сигнализировать читателю (и компилятору): "Это значение останется прежним, пока оно не исчезнет"
Это не имеет большого смысла без инициализации.
Конечно, можно было бы придумать что-то вроде val (3), которое позволяет три присваивания переменной, но я не думаю, что это было бы очень полезно.