Поиск…


Синтаксис

  • class Some [+ T] (значение: T) расширяет опцию [T]

  • объект None extends Option [Nothing]

  • Опция [T] (значение: T)

    Конструктор создает либо Some(value) либо None если это необходимо для предоставленного значения.

Варианты в виде коллекций

Option s имеет некоторые полезные функции более высокого порядка, которые можно легко понять, просматривая параметры как коллекции с нулевым или одним элементом - где None ведет себя как пустая коллекция, а Some(x) ведет себя как коллекция с одним элементом x .

val option: Option[String] = ???

option.map(_.trim) // None if option is None, Some(s.trim) if Some(s)
option.foreach(println) // prints the string if it exists, does nothing otherwise
option.forall(_.length > 4) // true if None or if Some(s) and s.length > 4
option.exists(_.length > 4) // true if Some(s) and s.length > 4
option.toList // returns an actual list

Использование опции вместо Null

В Java (и других языках) использование null является обычным способом указания того, что для ссылочной переменной не существует значения. В Scala использование Option предпочтительнее использования null . Option обнуляет значения, которые могут быть null .

None является подклассом Option обменивает нулевую ссылку. Some них являются подклассом Option обертывающей ненулевую ссылку.

Облегчение ссылки легко:

val nothing = Option(null) // None
val something = Option("Aren't options cool?") // Some("Aren't options cool?")

Это типичный код при вызове библиотеки Java, который может возвращать нулевую ссылку:

val resource = Option(JavaLib.getResource())
// if null, then resource = None
// else resource = Some(resource)

Если getResource() возвращает null значение, resource будет объектом None . В противном случае это будет объект Some(resource) . Предпочтительным способом обработки Option является использование функций более высокого порядка, доступных в типе Option . Например, если вы хотите проверить, не является ли ваше значение None (аналогично проверке, если value == null ), вы должны использовать функцию isDefined :

val resource: Option[Resource] = Option(JavaLib.getResource())
if (resource.isDefined) {  // resource is `Some(_)` type
  val r: Resource = resource.get
  r.connect()
}

Аналогично, для проверки null ссылки вы можете сделать это:

val resource: Option[Resource] = Option(JavaLib.getResource())
if (resource.isEmpty) { // resource is `None` type.
  System.out.println("Resource is empty! Cannot connect.")
}

Предпочтительно, чтобы вы лечить условное выполнение на обернутом значении в Option (без использования «» исключительного Option.get метода) путем обработки Option как монада и используя foreach :

val resource: Option[Resource] = Option(JavaLib.getResource())
resource foreach (r => r.connect())
// if r is defined, then r.connect() is run
// if r is empty, then it does nothing

Если требуется экземпляр Resource (по сравнению с экземпляром Option[Resource] ), вы все равно можете использовать Option для защиты от нулевых значений. Здесь метод getOrElse предоставляет значение по умолчанию:

lazy val defaultResource = new Resource()
val resource: Resource = Option(JavaLib.getResource()).getOrElse(defaultResource)

Java - код не будет легко обращаться в Scala Option , поэтому при передаче значений в Java коде это хорошая форма разворачивать в Option , передавая null или толкового по умолчанию в соответствующих случаях:

val resource: Option[Resource] = ???
JavaLib.sendResource(resource.orNull)
JavaLib.sendResource(resource.getOrElse(defaultResource)) // 

основы

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

Вариант - абстрактный класс с двумя детьми: Some и None .

Some содержат одно значение, а None содержит значения.

Option полезна в выражениях, которые в противном случае использовали бы null для представления отсутствия конкретного значения. Это защищает от NullPointerException и позволяет создавать множество выражений, которые могут не возвращать значение с использованием комбинаторов, таких как Map , FlatMap и т. Д.

Пример с картой

val countries = Map(
  "USA" -> "Washington",
  "UK" -> "London",
  "Germany" -> "Berlin",
  "Netherlands" -> "Amsterdam",
  "Japan" -> "Tokyo"
)
    
println(countries.get("USA")) // Some(Washington)
println(countries.get("France")) // None
println(countries.get("USA").get) // Washington
println(countries.get("France").get) // Error: NoSuchElementException
println(countries.get("USA").getOrElse("Nope")) // Washington
println(countries.get("France").getOrElse("Nope")) // Nope

Option[A] запечатана и, следовательно, не может быть расширена. Поэтому семантика стабильна и на нее можно положиться.

Варианты для понимания

Option s имеет метод flatMap . Это означает, что они могут использоваться для понимания. Таким образом, мы можем поднять регулярные функции для работы с Option s без необходимости их переопределения.

val firstOption: Option[Int] = Option(1)
val secondOption: Option[Int] = Option(2)

val myResult = for {
  firstValue <- firstOption
  secondValue <- secondOption
} yield firstValue + secondValue
// myResult: Option[Int] = Some(3)

Когда одно из значений равно None конечный результат вычисления также будет None .

val firstOption: Option[Int] = Option(1)
val secondOption: Option[Int] = None

val myResult = for {
  firstValue <- firstOption
  secondValue <- secondOption
} yield firstValue + secondValue
// myResult: Option[Int] = None

Примечание. Этот шаблон распространяется в более общем плане на понятия, называемые Monad s. (Дополнительная информация должна быть доступна на страницах, относящихся к пониманию и Monad s)

В общем случае невозможно смешивать разные монады в понимании. Но поскольку Option можно легко преобразовать в Iterable , мы можем легко смешать Option s и Iterable s, вызвав метод .toIterable .

val option: Option[Int] = Option(1)
val iterable: Iterable[Int] = Iterable(2, 3, 4, 5)

// does NOT compile since we cannot mix Monads in a for comprehension
// val myResult = for {
//   optionValue <- option
//   iterableValue <- iterable
//} yield optionValue + iterableValue

// It does compile when adding a .toIterable on the option
val myResult = for {
  optionValue <- option.toIterable
  iterableValue <- iterable
} yield optionValue + iterableValue
// myResult: Iterable[Int] = List(2, 3, 4, 5)

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



Modified text is an extract of the original Stack Overflow Documentation
Лицензировано согласно CC BY-SA 3.0
Не связан с Stack Overflow