Sök…


Syntax

  • klass Vissa [+ T] (värde: T) utökar Alternativ [T]

  • objekt Ingen utökar Alternativ [Ingenting]

  • Alternativ [T] (värde: T)

    Konstruktör för att skapa antingen ett Some(value) eller None som är lämpligt för det angivna värdet.

Alternativ som samlingar

Option har några användbara funktioner med högre ordning som lätt kan förstås genom att visa alternativ som samlingar med noll eller ett objekt - där None uppför sig som den tomma samlingen, och Some(x) uppför sig som en samling med ett enda objekt, 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

Använda alternativ istället för noll

I Java (och andra språk) är användning av null ett vanligt sätt att indikera att det inte finns något värde kopplat till en referensvariabel. I Scala föredras att använda Option framför att använda null . Option slår in värden som kan vara null .

None är en underklass av Option innehåller en nollreferens. Some är en underklass av Option innehåller en referens som inte är noll.

Att lägga in en referens är lätt:

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

Detta är en typisk kod när du ringer ett Java-bibliotek som kan returnera en nollreferens:

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

Om getResource() returnerar null värde resource kommer att bli en None objekt. Annars kommer det att vara ett Some(resource) objekt. Det föredragna sättet att hantera ett Option är att använda högre ordningsfunktioner tillgängliga inom Option . Om du till exempel vill kontrollera om ditt värde inte är None (liknande att kontrollera om value == null ) använder isDefined funktionen isDefined :

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

På samma sätt kan du göra för att leta efter en null :

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

Det föredras att du behandlar villkorad exekvering på det inslagna värdet för ett Option (utan att använda den "exceptionella" Option.get metoden) genom att behandla Option som en monad och använda 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

Om en Resource krävs (kontra en instans för Option[Resource] ) kan du fortfarande använda Option att skydda mot nollvärden. Här ger getOrElse metoden ett standardvärde:

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

Java-kod hanterar inte lätt Scalas Option , så när du överför värden till Java-kod är det bra form att ta bort ett Option , vidarebefordra null eller ett förnuftigt standard där det är lämpligt:

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

Grunderna

Ett Option är en datastruktur som innehåller antingen ett enda värde eller inget värde alls. Ett Option kan betraktas som samlingar av noll eller ett element.

Alternativet är en abstrakt klass med två barn: Some och None .

Some innehåller ett enda värde, och None innehåller inget värde.

Option är användbart i uttryck som annars skulle använda null att representera bristen på ett konkret värde. Detta skyddar mot en NullPointerException och tillåter sammansättningen av många uttryck som kanske inte returnerar ett värde med hjälp av kombinatorer som Map , FlatMap , etc.

Exempel med karta

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] är förseglat och kan därför inte förlängas. Därför är det semantik som är stabilt och kan lita på.

Alternativ för förståelser

Option har en flatMap metod. Det betyder att de kan användas i en förståelse. På detta sätt kan vi lyfta regelbundna funktioner för att arbeta med Option utan att behöva omdefiniera dem.

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)

När ett av värdena är ett None slutresultatet av beräkningen också 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

Obs: detta mönster sträcker sig mer generellt för begrepp som kallas Monad . (Mer information bör finnas tillgänglig på sidor som rör förståelser och Monad )

I allmänhet är det inte möjligt att blanda olika monader i en förståelse. Men eftersom Option kan lätt omvandlas till en Iterable , kan vi enkelt blanda Option s och Iterable s genom att anropa .toIterable metoden.

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)

En liten anmärkning: om vi hade definierat vårt för att förstå det andra sättet för förståelsen skulle sammanställas eftersom vårt alternativ skulle omvandlas implicit. Av den anledningen är det användbart att lägga till den här .toIterable (eller motsvarande funktion beroende på vilken samling du använder) för konsistens.



Modified text is an extract of the original Stack Overflow Documentation
Licensierat under CC BY-SA 3.0
Inte anslutet till Stack Overflow