Scala назначение vals

Почему это невозможно:

def main(args:Array[String]) {
    val whatever:String // Have it uninitialized here

    if(someCondition) {
        whatever = "final value" // Initialize it here
    }
}

Я не понимаю, почему это не должно быть законным. Я знаю, что могу сделать это var, но почему мы должны инициализировать val именно тогда, когда мы объявляем его? Не кажется ли более логичным возможность инициализировать его позже?

Ответ 1

Вы можете сделать:

  val whatever =
    if (someCondition)
      "final value"
    else
      "other value"

Ответ 2

Решение 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 вы обнаружите, что этот стиль помогает компилятору помочь вам, и вы должны найти себе программы с меньшим количеством ошибок!

Ответ 3

Используйте 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
}

Ответ 4

В дополнение к тому, что говорили другие, обратите внимание, что Java разрешает "пустые конечные" "переменные", что является особенностью, которую я пропустил:

final Boolean isItChristmasYet;

if (new Date().before(christmas)) {
    isItChristmasYet = Boolean.FALSE;
} else {
    isItChristmasYet = Boolean.TRUE;
}

Однако, благодаря анализу потока данных в компиляторе, javac не позволит вам оставить переменную whatever не назначенной, если someCondition не выполняется.

Ответ 5

Поскольку цель 'val' - сигнализировать читателю (и компилятору): "Это значение останется прежним, пока оно не исчезнет"

Это не имеет большого смысла без инициализации.

Конечно, можно было бы придумать что-то вроде val (3), которое позволяет три присваивания переменной, но я не думаю, что это было бы очень полезно.