Избегайте использования null

В большинстве языков есть специальное ключевое слово или объект, чтобы обозначить, что ссылка на объект — это "ничто". В Java это null. В Java null — это ключевое слово, а не объект, поэтому вызов любого метода у него недопустим. Но это запутанное решение для разработчиков языков. Почему возвращать ключевое слово, когда программист ожидает, что будет возвращён объект?

Тип Option в Scala

Чтобы сделать цель «всё является объектом» более согласованной и следовать соглашениям функционального программирования, Scala рекомендует использовать тип Option, когда переменные и возвращаемые значения функций могут не ссылаться ни на какое значение. Когда значения нет, используйте None — подкласс Option. Если есть значение, на которое нужно ссылаться, используйте Some, чтобы содержать это значение. Some также является подклассом Option. None объявлен как объект, а не класс, потому что нам нужен только один его экземпляр. Таким образом, он несколько похож на ключевое слово null, но это настоящий объект с методами.

Примеры использования

Значения типа Option часто используются как возвращаемые типы операций над типами коллекций Scala (List, Map и т. д.). Например, метод get у Map:

scala> val capitals = Map("France"->"Paris", "Japan"->"Tokyo", "China"->"Beijing")
capitals: scala.collection.immutable.Map[String,String] = Map(France -> Paris, Japan -> Tokyo, China -> Beijing)

scala> capitals get "France"
res0: Option[String] = Some(Paris)

scala> capitals get "North Pole"
res1: Option[String] = None

У Option есть две подкатегории: Some и None. Когда программа возвращает Some, это означает, что функция успешно вернула вам String, и вы можете получить эту String с помощью функции get(). Если программа возвращает None, это означает, что строки для вас нет.

Когда он возвращает None, то есть когда для вас нет String, если вы всё же настаиваете на вызове get() для получения String, Scala также выбросит вам NoSuchElementException. Мы также можем выбрать другой метод — getOrElse. Этот метод возвращает соответствующее значение, когда Option является экземпляром Some, и возвращает переданный параметр, когда он является экземпляром None. Иными словами, параметр, переданный в getOrElse, на самом деле является значением, возвращаемым по умолчанию.
scala> capitals get "North Pole" get
warning: there was one feature warning; re-run with -feature for details
java.util.NoSuchElementException: None.get
  at scala.None$.get(Option.scala:347)
  at scala.None$.get(Option.scala:345)
  ... 33 elided

scala> capitals get "France" get
warning: there was one feature warning; re-run with -feature for details
res3: String = Paris

scala> (capitals get "North Pole") getOrElse "Oops"
res7: String = Oops

scala> capitals get "France" getOrElse "Oops"
res8: String = Paris

Отделите опциональное значение с помощью сопоставления с образцом. Если сопоставленное значение — Some, извлеките значение внутри Some и присвойте его переменной x:

def showCapital(x: Option[String]) = x match {
    case Some(s) => s
    case None => "?"
}

Советы

В программах на Scala Option используется очень часто. В Java для представления пустых значений используется null, и во многих местах кода приходится добавлять проверки на ключевое слово null, иначе легко может возникнуть NullPointerException. Поэтому программам на Java нужно заботиться о том, какие переменные могут быть null. Вероятность того, что эти переменные будут null, очень мала, но как только это происходит, трудно выяснить, почему возникло NullPointerException. Тип Option в Scala позволяет избежать этой ситуации, поэтому в приложениях на Scala рекомендуется использовать тип Option для представления некоторых опциональных значений. Благодаря типу Option читатели с первого взгляда могут увидеть, что значения этого типа могут быть None.

Фактически, благодаря статической типизации Scala вы не можете по ошибке попытаться вызвать метод у значения, которое может быть null. Хотя в Java эту ошибку легко совершить, в Scala она не компилируется. Это происходит потому, что практика программирования, при которой в Java не проверяется, является ли переменная null, в Scala становится ошибкой типа (нельзя использовать Option[String] как String). Поэтому использование Option настоятельно поощряет более гибкие привычки программирования.

Подробное объяснение Option[T]

В Scala Option[T] на самом деле является контейнером, точно так же, как массив или List. Его можно рассматривать как List, который может содержать ноль или один элемент.

Когда внутри вашего Option что-то есть, длина этого List равна 1 (то есть Some), а когда внутри вашего Option ничего нет, его длина равна 0 (то есть None).

цикл for

Если мы используем Option как обычный List и с помощью цикла for перебираем этот Option, то, если Option равен None, код внутри цикла for, естественно, не выполнится, и таким образом мы достигаем цели «не проверять, равен ли Option None».

scala> val map1 = Map("key1" -> "value1")
map1: scala.collection.immutable.Map[String,String] = Map(key1 -> value1)

scala> val value1 = map1.get("key1")
value1: Option[String] = Some(value1)

scala> val value2 = map1.get("key2")
value2: Option[String] = None

scala> def printContentLength(x: Option[String]) {
     |   for (c <- x){
     |     println(c.length)
     |   }
     | }
printContentLength: (x: Option[String])Unit

scala> printContentLength(value1)
6

scala> printContentLength(value2)

операция map

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

Теперь давайте рассмотрим, как с помощью метода map у Option реализоватьlength: xxxФорма вывода такова:

Сначала вычислите длину строки внутри контейнера Option

Затем добавьте текст "length: " перед длиной

Наконец, один раз пройдите по контейнеру и выведите то, что находится внутри него.

scala> value1.map(_.length).map("length: " + _).foreach(println)
length: 6

scala> value1.map("length: " + _.length).foreach(println)
length: 6

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

Ссылка на оригинал: https://www.jianshu.com/p/95896d06a94d