Was ist der Sinn der Klasse Option [T]?


83

Ich kann den Punkt der Option[T]Klasse in Scala nicht verstehen . Ich meine, ich kann keine Vorteile von Noneüber sehen null.

Betrachten Sie zum Beispiel den Code:

object Main{
  class Person(name: String, var age: int){
    def display = println(name+" "+age)
  }

  def getPerson1: Person = {
    // returns a Person instance or null
  }

  def getPerson2: Option[Person] = {
    // returns either Some[Person] or None
  }

  def main(argv: Array[String]): Unit = {
    val p = getPerson1
    if (p!=null) p.display

    getPerson2 match{
      case Some(person) => person.display
      case None => /* Do nothing */
    }
  }
}

Angenommen, die Methode getPerson1kehrt zurück null, und der Aufruf in der displayersten Zeile von mainschlägt mit fehl NPE. In ähnlicher Weise getPerson2schlägt Noneder displayAufruf bei einem ähnlichen Fehler erneut fehl, wenn er zurückkehrt .

Wenn ja, warum erschwert Scala die Dinge, indem ein neuer Value Wrapper ( Option[T]) eingeführt wird, anstatt einem einfachen Ansatz zu folgen, der in Java verwendet wird?

AKTUALISIEREN:

Ich habe meinen Code gemäß dem Vorschlag von @Mitch bearbeitet . Ich sehe immer noch keinen besonderen Vorteil von Option[T]. Ich muss auf das Außergewöhnliche nulloder Nonein beiden Fällen testen . :(

Wenn ich die Antwort von @ Michael richtig verstanden habe , besteht der einzige Vorteil Option[T]darin, dass der Programmierer explizit darüber informiert wird, dass diese Methode None zurückgeben kann . Ist dies der einzige Grund für diese Designentscheidung?


23
Tatsächlich wird die "get" -Methode in Option [T] ausgesprochen: "Warum zum Teufel passen Sie nicht zu diesem Muster?"
Mitch Blevins

2
Mitch hat recht. Versuchen Sie Ihr Beispiel neu zu formulieren , ohne get, und Sie werden bekommen es. :-)
Daniel C. Sobral

Sie haben Person p .. das ist java .. .try val p = ... Außerdem gibt es mehr zu Option, wie von Daniel und Synesso unten gezeigt - einige großartige Antworten hier.
Michael Neale

@ Michael: Ups! Danke fürs Zeigen; korrigierte es.
fehlender Faktor

Antworten:


72

Sie werden es Optionbesser machen, wenn Sie sich zwingen, es niemals zu benutzen get. Das liegt daran, dass getes das Äquivalent von "ok, schick mich zurück ins Nullland" ist.

Nehmen Sie also Ihr Beispiel. Wie würden Sie displayohne Verwendung anrufen get? Hier sind ein paar alternativen:

getPerson2 foreach (_.display)
for (person <- getPerson2) person.display
getPerson2 match {
  case Some(person) => person.display
  case _ =>
}
getPerson2.getOrElse(Person("Unknown", 0)).display

Mit keiner dieser Alternativen können Sie displayetwas aufrufen , das nicht existiert.

Was , warum getexistiert, ist Scala nicht sagen Ihnen , wie Sie den Code geschrieben werden soll. Es mag Sie sanft anstoßen, aber wenn Sie auf kein Sicherheitsnetz zurückgreifen möchten, haben Sie die Wahl.


Du hast es hier geschafft:

ist der einzige Vorteil von Option [T], dass sie dem Programmierer explizit mitteilt, dass diese Methode None zurückgeben könnte?

Bis auf das "einzige". Aber lassen Sie mich neu formulieren , dass in einer anderen Art und Weise: Der Hauptvorteil von Option[T]über Tist Typsicherheit. Es stellt sicher, dass Sie keine TMethode an ein Objekt senden , das möglicherweise nicht vorhanden ist, da der Compiler dies nicht zulässt.

Sie sagten, Sie müssen in beiden Fällen auf Nullfähigkeit testen, aber wenn Sie vergessen oder nicht wissen, dass Sie auf Null prüfen müssen, wird Ihnen der Compiler dies mitteilen? Oder werden Ihre Benutzer?

Natürlich erlaubt Scala aufgrund seiner Interoperabilität mit Java genau wie Java Nullen. Wenn Sie also Java-Bibliotheken verwenden, wenn Sie schlecht geschriebene Scala-Bibliotheken verwenden oder wenn Sie schlecht geschriebene persönliche Scala-Bibliotheken verwenden, müssen Sie sich immer noch mit Nullzeigern befassen.

Weitere zwei wichtige Vorteile, die Optionmir einfallen, sind:

  • Dokumentation: Eine Signatur vom Methodentyp gibt an, ob ein Objekt immer zurückgegeben wird oder nicht.

  • Monadische Kompositionsfähigkeit.

Letzteres dauert viel länger, um es vollständig zu verstehen, und es eignet sich nicht gut für einfache Beispiele, da es nur seine Stärke in komplexem Code zeigt. Also, ich werde unten ein Beispiel geben, aber ich bin mir bewusst, dass es kaum etwas bedeuten wird, außer für die Leute, die es bereits bekommen.

for {
  person <- getUsers
  email <- person.getEmail // Assuming getEmail returns Option[String]
} yield (person, email)

5
"zwinge dich, niemals zu benutzen get" -> Also, mit anderen Worten: "Du tust es nicht get!" :)
Fredoverflow

31

Vergleichen Sie:

val p = getPerson1 // a potentially null Person
val favouriteColour = if (p == null) p.favouriteColour else null

mit:

val p = getPerson2 // an Option[Person]
val favouriteColour = p.map(_.favouriteColour)

Die monadische Eigenschaft binden , die in Scala als erscheint Kartenfunktion ermöglicht es uns, Chain - Operationen auf Objekten ohne sich Gedanken darüber , ob es sich um ‚Null‘ oder nicht.

Nehmen Sie dieses einfache Beispiel etwas weiter. Angenommen, wir wollten alle Lieblingsfarben einer Personenliste finden.

// list of (potentially null) Persons
for (person <- listOfPeople) yield if (person == null) null else person.favouriteColour

// list of Options[Person]
listOfPeople.map(_.map(_.favouriteColour))
listOfPeople.flatMap(_.map(_.favouriteColour)) // discards all None's

Oder vielleicht möchten wir den Namen der Schwester der Mutter eines Vaters finden:

// with potential nulls
val father = if (person == null) null else person.father
val mother = if (father == null) null else father.mother
val sister = if (mother == null) null else mother.sister

// with options
val fathersMothersSister = getPerson2.flatMap(_.father).flatMap(_.mother).flatMap(_.sister)

Ich hoffe, dies gibt Aufschluss darüber, wie Optionen das Leben ein wenig erleichtern können.


Was ist in Ihrem letzten Beispiel, wenn der Vater einer Person null ist? mapwird zurückkehren Noneund der Anruf würde mit einem Fehler fehlschlagen. Wie ist es besser als der nullAnsatz?
fehlender Faktor

5
Nein. Wenn die Person Keine ist (oder Vater, Mutter oder Schwester), ist Väter Mutter Schwester Keine, aber es wird kein Fehler ausgegeben.
Paradigmatisch

6
Ich denke du meinst flatMap und nicht map.
Retronym

Danke für die Bearbeitung Daniel. Ich habe den Code nicht ausprobiert, bevor ich ihn veröffentlicht habe. Wird es beim nächsten Mal besser machen.
Synesso

2
val favouriteColour = if (p == null) p.favouriteColour else null // genau der Fehler, den Option Ihnen hilft, zu vermeiden! Diese Antwort ist seit Jahren hier, ohne dass jemand diesen Fehler entdeckt!
Mark Lister

22

Der Unterschied ist subtil. Denken Sie daran, um wirklich eine Funktion zu sein , die einen Wert zurückgeben muss - null wird in diesem Sinne nicht wirklich als "normaler Rückgabewert" betrachtet, sondern eher als unterer Typ / nichts.

In praktischer Hinsicht würden Sie jedoch Folgendes tun, wenn Sie eine Funktion aufrufen, die optional etwas zurückgibt:

getPerson2 match {
   case Some(person) => //handle a person
   case None => //handle nothing 
}

Zugegeben, Sie können mit null etwas Ähnliches tun - aber dies macht die Semantik des Aufrufs getPerson2aufgrund der Tatsache, dass es zurückkehrt , offensichtlich Option[Person](eine nette praktische Sache, außer sich darauf zu verlassen, dass jemand das Dokument liest und eine NPE erhält, weil er das nicht liest doc).

Ich werde versuchen, einen funktionierenden Programmierer zu finden, der eine strengere Antwort geben kann als ich.


1
Dies ist auch mein Verständnis von Option. Es sagt dem Programmierer ausdrücklich, dass wir eine None bekommen könnten, und wenn Sie dumm genug sind, sich daran zu erinnern, Some (T) zu machen, aber nicht die None zu fangen, sind Sie in Schwierigkeiten.
Cflewis

1
Lewisham - Ich denke, der Compiler wird Sie warnen, da Some / None einen algebraischen Datentyp bilden (abstraktes versiegeltes Merkmal ...) (aber ich gehe hier aus dem Gedächtnis).
Michael Neale

6
Der Punkt des Optionstyps in den meisten Sprachen, die ihn verwenden, besteht darin, dass anstelle einer Laufzeit-Null-Ausnahme ein Fehler vom Typ der Kompilierungszeit angezeigt wird. Der Compiler kann erkennen, dass Sie bei Verwendung der Daten keine Aktion für die Bedingung Keine haben, was der Fall sein sollte ein Typfehler sein.
Justin Smith

15

Für mich sind Optionen wirklich interessant, wenn sie zum Verständnis der Syntax verwendet werden. Unter synesso vorhergehenden Beispiel:

// with potential nulls
val father = if (person == null) null else person.father
val mother = if (father == null) null else father.mother
val sister = if (mother == null) null else mother.sister

// with options
val fathersMothersSister = for {
                                  father <- person.father
                                  mother <- father.mother
                                  sister <- mother.sister
                               } yield sister

Wenn eine der Zuweisungen vorhanden ist None, fathersMothersSisterwird die Zuweisung Nonejedoch nicht NullPointerExceptionerhöht. Sie können dann sicher fathersMothersSisteran eine Funktion übergeben, die Optionsparameter übernimmt, ohne sich Sorgen machen zu müssen. Sie suchen also nicht nach Null und kümmern sich nicht um Ausnahmen. Vergleichen Sie dies mit der Java-Version im Synesso- Beispiel.


3
Es ist eine Schande, dass sich die <-Syntax in Scala auf die "Listenverständnis-Syntax" beschränkt, da sie tatsächlich mit der allgemeineren doSyntax von Haskell oder der domonadForm aus Clojures Monadenbibliothek identisch ist . Das Binden an Listen verkauft es kurz.
seh

11
"Zum Verständnis" in Scala ist im Wesentlichen das "Do" in Haskell. Sie sind nicht auf Listen beschränkt. Sie können alles verwenden, was implementiert wird: def map [B] (f: A => B): C [B] def flatMap [B] (f: A => C [B]): C [B] Def-Filter (p: A => Boolescher Wert): C [A]. IOW, jede Monade
GClaramunt

2
@seh Ich habe @ GClaramunts Kommentar positiv bewertet, aber ich kann seinen Standpunkt nicht genug betonen. In Scala gibt es keinen Zusammenhang zwischen Verständnis und Listen - außer dass letztere mit ersteren verwendbar sind. Ich verweise Sie auf stackoverflow.com/questions/1052476/… .
Daniel C. Sobral

Ja, ich weiß, dass es keine Beziehung gibt, aber ich stimme zu, dass es sich lohnt, darauf hinzuweisen. Ich habe die erste Zeile dieser Antwort kommentiert, in der paradigmatisch "Listenverständnis-Syntax" erwähnt wird. Es ist ein Unterrichtsproblem im Gegensatz zu einem Sprachdesignproblem.
seh

9

Mit Option haben Sie ziemlich mächtige Kompositionsmöglichkeiten:

def getURL : Option[URL]
def getDefaultURL : Option[URL]


val (host,port) = (getURL orElse getDefaultURL).map( url => (url.getHost,url.getPort) ).getOrElse( throw new IllegalStateException("No URL defined") )

Könnten Sie das vollständig erklären?
Jesvin Jose

8

Vielleicht hat jemand anderes darauf hingewiesen, aber ich habe es nicht gesehen:

Ein Vorteil des Mustervergleichs mit Option [T] gegenüber der Nullprüfung besteht darin, dass Option eine versiegelte Klasse ist. Daher gibt der Scala-Compiler eine Warnung aus, wenn Sie den Fall Some oder None nicht codieren. Der Compiler verfügt über ein Compiler-Flag, das Warnungen in Fehler umwandelt. Auf diese Weise kann verhindert werden, dass der Fall "nicht vorhanden" nicht zur Laufzeit, sondern zur Laufzeit behandelt wird. Dies ist ein enormer Vorteil gegenüber der Verwendung des Nullwerts.


7

Es ist nicht dazu da, eine Nullprüfung zu vermeiden, sondern eine Nullprüfung zu erzwingen. Der Punkt wird klar, wenn Ihre Klasse 10 Felder hat, von denen zwei null sein können. Und Ihr System hat 50 ähnliche Klassen. In der Java-Welt versuchen Sie, NPEs in diesen Feldern zu verhindern, indem Sie eine Kombination aus geistiger Leistungsfähigkeit, Namenskonvention oder vielleicht sogar Anmerkungen verwenden. Und jeder Java-Entwickler scheitert daran in erheblichem Maße. Die Option-Klasse macht nicht nur Entwicklern, die versuchen, den Code zu verstehen, "nullfähige" Werte visuell klar, sondern ermöglicht dem Compiler auch, diesen zuvor unausgesprochenen Vertrag durchzusetzen.


6

[kopiert von diesem Kommentar von Daniel Spiewak ]

Wenn die einzige Möglichkeit darin Optionbestand, Musterübereinstimmungen durchzuführen, um Werte herauszuholen, dann stimme ich zu, dass sie sich gegenüber Null überhaupt nicht verbessern. Sie vermissen jedoch eine * riesige * Klasse ihrer Funktionalität. Der einzige zwingende Grund für die Verwendung Optionist, wenn Sie die Dienstprogrammfunktionen höherer Ordnung verwenden. Tatsächlich müssen Sie seine monadische Natur nutzen. Zum Beispiel (unter der Annahme einer bestimmten Menge an API-Trimmen):

val row: Option[Row] = database fetchRowById 42
val key: Option[String] = row flatMap { _ get port_key }
val value: Option[MyType] = key flatMap (myMap get)
val result: MyType = value getOrElse defaultValue

War das nicht schick? Wir können es tatsächlich viel besser machen, wenn wir forVerständnis verwenden:

val value = for {
row <- database fetchRowById 42
key <- row get "port_key"
value <- myMap get key
} yield value
val result = value getOrElse defaultValue

Sie werden feststellen, dass wir * nie * explizit nach null, None oder einem seiner Typen suchen. Der springende Punkt bei Option ist, diese Überprüfung zu vermeiden. Sie reihen einfach Berechnungen entlang und bewegen sich die Linie entlang, bis Sie * wirklich * einen Wert herausholen müssen. An diesem Punkt können Sie entscheiden, ob Sie eine explizite Überprüfung durchführen möchten (was Sie niemals tun sollten), einen Standardwert angeben, eine Ausnahme auslösen usw.

Ich mache niemals explizite Übereinstimmungen Optionmit und ich kenne viele andere Scala-Entwickler, die im selben Boot sitzen. David Pollak erwähnte mir neulich, dass er einen solchen expliziten Abgleich Option(oder Boxim Fall von Lift) als Zeichen dafür verwendet, dass der Entwickler, der den Code geschrieben hat, die Sprache und ihre Standardbibliothek nicht vollständig versteht.

Ich will kein Trollhammer sein, aber Sie müssen sich wirklich ansehen, wie Sprachfunktionen in der Praxis * tatsächlich * verwendet werden, bevor Sie sie als nutzlos verprügeln. Ich stimme absolut zu, dass Option nicht überzeugend ist, da * Sie * es verwendet haben, aber Sie verwenden es nicht so, wie es entworfen wurde.


Es ist eine traurige Folge hier: es gibt keinen Sprung-basierter Kurzschluss im Spiel, so dass jede aufeinanderfolgende Anweisung prüft die Optionfür Nonewieder. Wären die Anweisungen als verschachtelte Bedingungen geschrieben worden, würde jeder potenzielle "Fehler" nur einmal getestet und bearbeitet. In Ihrem Beispiel über das Ergebnis fetchRowByIdeffektiv kontrolliert wird drei Mal: einmal führen key‚s Initialisierung wieder für value‘ s, und schließlich für result‚s. Es ist eine elegante Art, es zu schreiben, aber es ist nicht ohne Laufzeitkosten.
seh

4
Ich denke, Sie verstehen Scalas Verständnis falsch. Das zweite Beispiel ist nachdrücklich KEINE Schleife, sondern wird vom Compiler in eine Reihe von flatMap-Operationen übersetzt - wie im ersten Beispiel.
Kevin Wright

Es ist lange her, dass ich meinen Kommentar hier geschrieben habe, aber ich habe gerade Kevins gesehen. Kevin, auf wen haben Sie sich bezogen, als Sie geschrieben haben: "Sie missverstehen?" Ich sehe nicht , wie es gewesen sein könnte ich , wie ich noch nie etwas von einer Schleife erwähnt.
seh

6

Ein Punkt, den hier sonst niemand angesprochen zu haben scheint, ist, dass Sie zwar eine Nullreferenz haben können, aber durch Option eine Unterscheidung eingeführt wird.

Das heißt, Sie können haben Option[Option[A]], die von bewohnt würden None, Some(None)und Some(Some(a))wo aist einer der üblichen Bewohner von A. Dies bedeutet, dass Sie, wenn Sie eine Art Container haben und in der Lage sein möchten, Nullzeiger darin zu speichern und sie herauszuholen, einen zusätzlichen booleschen Wert zurückgeben müssen, um zu wissen, ob Sie tatsächlich einen Wert herausbekommen haben. Warzen wie diese sind in den Java-Container-APIs im Überfluss vorhanden, und einige sperrfreie Varianten können sie nicht einmal bereitstellen.

null ist eine einmalige Konstruktion, komponiert nicht mit sich selbst, ist nur für Referenztypen verfügbar und zwingt Sie dazu, nicht vollständig zu argumentieren.

Zum Beispiel, wenn Sie überprüfen

if (x == null) ...
else x.foo()

Sie müssen in Ihrem Kopf in der gesamten elseBranche herumtragen, dass x != nullund dass dies bereits überprüft wurde. Wenn Sie jedoch so etwas wie eine Option verwenden

x match {
case None => ...
case Some(y) => y.foo
}

Sie wissen, dass Sie nicht Nonekonstruktionsbedingt sind - und Sie würden wissen null, dass dies auch nicht der Fall war , wenn Hoares Milliarden-Dollar-Fehler nicht gewesen wäre .


3

Option [T] ist eine Monade, die sehr nützlich ist, wenn Sie Funktionen höherer Ordnung zum Bearbeiten von Werten verwenden.

Ich schlage vor, dass Sie die unten aufgeführten Artikel lesen. Es sind wirklich gute Artikel, die Ihnen zeigen, warum Option [T] nützlich ist und wie es auf funktionale Weise verwendet werden kann.


Ich werde der empfohlenen Leseliste Tony Morris 'kürzlich veröffentlichtes Tutorial "Was bedeutet Monade?" Hinzufügen: projects.tmorris.net/public/what-does-monad-mean/artifacts/1.0/…
Randall Schulz

3

Um Randalls Teaser einer Antwort zu ergänzen und zu verstehen, warum das potenzielle Fehlen eines Wertes durch dargestellt wird, Optionmuss man verstehen, was Optionmit vielen anderen Typen in Scala gemeinsam ist - insbesondere mit Typen, die Monaden modellieren. Wenn einer das Fehlen eines Wertes mit null darstellt, kann diese Unterscheidung zwischen Abwesenheit und Anwesenheit nicht an den Verträgen teilnehmen, die von den anderen monadischen Typen geteilt werden.

Wenn Sie nicht wissen, was Monaden sind, oder wenn Sie nicht bemerken, wie sie in Scalas Bibliothek dargestellt werden, werden Sie nicht sehen, was Optionmitspielt, und Sie können nicht sehen, was Sie verpassen. Es gibt viele Vorteile bei der Verwendung Optionanstelle von Null, die selbst ohne ein Monadenkonzept bemerkenswert wären (ich diskutiere einige davon im Scala-Benutzer- Mailinglisten-Thread "Kosten der Option / Einige gegen Null" hier ), aber ich spreche darüber Die Isolation ist so, als würde man über den Iteratortyp einer bestimmten Implementierung einer verknüpften Liste sprechen und sich fragen, warum dies notwendig ist, während die allgemeinere Schnittstelle zwischen Container, Iterator und Algorithmus fehlt. Auch hier gibt es eine breitere Oberfläche.Option


Vielen Dank für den Link. Es war wirklich nützlich. :)
fehlender Faktor

Ihr Kommentar zum Thread war so prägnant, dass ich seinen Punkt fast verfehlt hätte. Ich wünschte wirklich, Null könnte verboten werden.
Alain O'Dea

2

Ich denke, der Schlüssel liegt in Synessos Antwort: Option ist nicht in erster Linie als umständlicher Alias ​​für Null nützlich, sondern als vollwertiges Objekt, das Ihnen dann bei Ihrer Logik helfen kann.

Das Problem mit null ist, dass es das Fehlen eines Objekts ist. Es gibt keine Methoden, die Ihnen dabei helfen könnten (obwohl Sie als Sprachdesigner Ihrer Sprache immer längere Listen von Funktionen hinzufügen können, die ein Objekt emulieren, wenn Sie wirklich Lust dazu haben).

Wie Sie gezeigt haben, kann Option eine Null emulieren. Sie müssen dann den außergewöhnlichen Wert "Keine" anstelle des außergewöhnlichen Werts "null" testen. Wenn Sie vergessen, werden in beiden Fällen schlimme Dinge passieren. Option macht es weniger wahrscheinlich, dass es versehentlich passiert, da Sie "get" eingeben müssen (was Sie daran erinnern sollte, dass es null sein könnte , ähm, ich meine None), aber dies ist ein kleiner Vorteil im Austausch für ein zusätzliches Wrapper-Objekt .

Wo Option wirklich anfängt, seine Macht zu zeigen, hilft es Ihnen, mit dem Konzept von Ich-wollte-etwas-aber-ich-habe-nicht-tatsächlich-eins umzugehen.

Betrachten wir einige Dinge, die Sie möglicherweise mit Dingen tun möchten, die möglicherweise null sind.

Vielleicht möchten Sie einen Standardwert festlegen, wenn Sie eine Null haben. Vergleichen wir Java und Scala:

String s = (input==null) ? "(undefined)" : input;
val s = input getOrElse "(undefined)"

Anstelle eines etwas umständlichen ?: Konstrukts haben wir eine Methode, die sich mit der Idee befasst, "einen Standardwert zu verwenden, wenn ich null bin". Dies bereinigt Ihren Code ein wenig.

Vielleicht möchten Sie nur dann ein neues Objekt erstellen, wenn Sie einen echten Wert haben. Vergleichen Sie:

File f = (filename==null) ? null : new File(filename);
val f = filename map (new File(_))

Scala ist etwas kürzer und vermeidet wiederum Fehlerquellen. Betrachten Sie dann den kumulativen Nutzen, wenn Sie Dinge miteinander verketten müssen, wie in den Beispielen von Synesso, Daniel und paradigmatisch gezeigt.

Es ist keine große Verbesserung, aber wenn Sie alles zusammenzählen, lohnt es sich überall, sehr leistungsstarken Code zu speichern (wo Sie selbst den winzigen Aufwand beim Erstellen des Some (x) -Wrapper-Objekts vermeiden möchten).

Die Verwendung von Übereinstimmungen ist für sich genommen nicht wirklich hilfreich, außer als Gerät, um Sie über den Fall null / keine zu informieren. Wenn es wirklich hilfreich ist, wenn Sie anfangen, es zu verketten, z. B. wenn Sie eine Liste von Optionen haben:

val a = List(Some("Hi"),None,Some("Bye"));
a match {
  case List(Some(x),_*) => println("We started with " + x)
  case _ => println("Nothing to start with.")
}

Jetzt können Sie die Fälle "Keine" und "Fälle ohne Liste" in einer praktischen Anweisung zusammenfassen, die genau den gewünschten Wert herausholt.


2

Null-Rückgabewerte sind nur aus Kompatibilitätsgründen mit Java vorhanden. Sie sollten sie nicht anderweitig verwenden.


1

Es ist wirklich eine Frage des Programmierstils. Wenn Sie Functional Java verwenden oder Ihre eigenen Hilfsmethoden schreiben, können Sie Ihre Option-Funktionalität nutzen, ohne die Java-Sprache aufzugeben:

http://functionaljava.org/examples/#Option.bind

Nur weil Scala es standardmäßig enthält, ist es nichts Besonderes. Die meisten Aspekte funktionaler Sprachen sind in dieser Bibliothek verfügbar und können gut mit anderem Java-Code koexistieren. So wie Sie Scala mit Nullen programmieren können, können Sie auch Java ohne Nullen programmieren.


0

Option gibt im Voraus zu, dass es sich um eine glatte Antwort handelt, und ist eine Monade.


Ich weiß, dass es eine Monade ist. Warum sollte ich sonst ein fragliches "Monaden" -Tag einfügen?
fehlender Faktor

^ Die obige Aussage bedeutet nicht, dass ich verstehe, was eine Monade ist. : D
fehlender Faktor

4
Monaden sind cool. Wenn du sie nicht benutzt oder zumindest nicht vorgibst zu verstehen, dann bist du nicht cool ;-)
paradigmatisch

0

Eigentlich teile ich den Zweifel mit Ihnen. Über Option stört es mich wirklich, dass 1) es einen Leistungsaufwand gibt, da es eine Menge von "einigen" Wrappern gibt, die überall erstellt werden. 2) Ich muss viel Some und Option in meinem Code verwenden.

Um die Vor- und Nachteile dieser Sprachentwurfsentscheidung zu erkennen, sollten wir Alternativen in Betracht ziehen. Da Java das Problem der Nullfähigkeit einfach ignoriert, ist es keine Alternative. Die eigentliche Alternative bietet die Programmiersprache Fantom. Es gibt dort nullbare und nicht nullbare Typen und ?. ?: Operatoren anstelle von Scalas map / flatMap / getOrElse. Im Vergleich sehe ich folgende Aufzählungszeichen:

Vorteil der Option:

  1. einfachere Sprache - keine zusätzlichen Sprachkonstrukte erforderlich
  2. Uniform mit anderen monadischen Typen

Nullables Vorteil:

  1. in typischen Fällen kürzere Syntax
  2. Bessere Leistung (da Sie keine neuen Optionsobjekte und Lambdas für map, flatMap erstellen müssen)

Es gibt hier also keinen offensichtlichen Gewinner. Und noch eine Anmerkung. Es gibt keinen syntaktischen Hauptvorteil für die Verwendung von Option. Sie können Folgendes definieren:

def nullableMap[T](value: T, f: T => T) = if (value == null) null else f(value)

Oder verwenden Sie einige implizite Konvertierungen, um eine Pritty-Syntax mit Punkten zu erhalten.


Hat jemand solide Benchmarks für den Leistungseinbruch einer modernen VM erstellt? Escape-Analyse bedeutet, dass viele temporäre Optionsobjekte auf dem Stapel zugewiesen werden können (viel billiger als der Heap), und der Generations-GC verarbeitet etwas weniger temporäre Objekte ziemlich effizient. Wenn Geschwindigkeit für Ihr Projekt wichtiger ist als die Vermeidung von NPEs, sind Optionen wahrscheinlich nicht für Sie.
Justin W

Erwähnen Sie keinen Leistungsaufwand ohne Zahlen, um ihn zu sichern. Dies ist ein äußerst häufiger Fehler, wenn gegen Abstraktionen wie Option argumentiert wird. Ich werde meine Ablehnung gerne rückgängig machen, wenn Sie auf einen Benchmark verweisen oder diesen veröffentlichen oder den Leistungskommentar entfernen :)
Alain O'Dea

0

Der eigentliche Vorteil expliziter Optionstypen besteht darin, dass Sie sie nicht an 98% aller Stellen verwenden können und somit Nullausnahmen statisch ausschließen. (Und bei den anderen 2% erinnert Sie das Typsystem daran, ordnungsgemäß zu überprüfen, wann Sie tatsächlich darauf zugreifen.)


-3

Eine andere Situation, in der Option funktioniert, ist in Situationen, in denen Typen keinen Nullwert haben können. Es ist nicht möglich, Null in einem Int-, Float-, Double- usw. Wert zu speichern, aber mit einer Option können Sie None verwenden.

In Java müssten Sie die Box-Versionen (Integer, ...) dieser Typen verwenden.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.