Scala Language
Класс опций
Поиск…
Синтаксис
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 (или соответствующую функцию в зависимости от того, какую коллекцию вы используете) для согласованности.