Ich habe einen Artikel gelesen, der aus einer Slashdot-Geschichte stammt, und bin auf diesen kleinen Leckerbissen gestoßen:
Nehmen Sie die neueste Version von Java, die versucht, die Überprüfung von Nullzeigern zu vereinfachen, indem sie eine Kurzsyntax für das Testen endloser Zeiger bietet. Das Hinzufügen eines Fragezeichens zu jedem Methodenaufruf enthält automatisch einen Test für Nullzeiger, der das Nest einer Wenn-Dann-Anweisung einer Ratte ersetzt, wie z.
public String getPostcode(Person person) { String ans= null; if (person != null) { Name nm= person.getName(); if (nm!= null) { ans= nm.getPostcode(); } } return ans }
Mit diesem:
public String getFirstName(Person person) { return person?.getName()?.getGivenName(); }
Ich habe das Internet durchsucht (okay, ich habe mindestens 15 Minuten damit verbracht, Variationen von "Java-Fragezeichen" zu googeln) und nichts bekommen. Meine Frage: Gibt es dazu eine offizielle Dokumentation? Ich habe festgestellt, dass C # einen ähnlichen Operator hat (den Operator "??"), aber ich möchte die Dokumentation für die Sprache erhalten, in der ich arbeite. Oder ist dies nur eine Verwendung des ternären Operators, den ich habe noch nie vorher gesehen.
Vielen Dank!
BEARBEITEN: Link zum Artikel: http://infoworld.com/d/developer-world/12-programming-mistakes-avoid-292
A ?? B == (A != null) ? A : B
. Dies scheint eine Eigenschaft für ein Objekt auszuwerten, wenn die Objektreferenz nicht null ist, dA?.B == (A != null) ? A.B : null
. H.Antworten:
Die ursprüngliche Idee kommt von groovy. Es wurde für Java 7 als Teil von Project Coin vorgeschlagen: https://wiki.openjdk.java.net/display/Coin/2009+Proposals+TOC (Elvis und andere null-sichere Operatoren), wurde jedoch noch nicht akzeptiert .
Der verwandte Elvis-Operator ?: Wurde vorgeschlagen, um eine
x ?: y
Abkürzung für zu verwendenx != null ? x : y
, die besonders nützlich ist, wenn x ein komplexer Ausdruck ist.quelle
x!=null ? x : y
?
ist Elvis Presleys Unterschriftenquiff; Das:
Just repräsentiert wie gewohnt ein Paar Augen. Vielleicht?:-o
ist es eindrucksvoller ...?:0
Sollte ein Operator "Nicht null oder 0" sein. Mach es so.Diese Syntax existiert weder in Java noch soll sie in einer der mir bekannten kommenden Versionen enthalten sein.
quelle
Optional
undmap
Sachen zu verwenden. Wir verbergen das Problem nicht, wenn der Wert nullbar ist, was bedeutet, dass manchmal erwartet wird, dass er null ist, und Sie müssen damit umgehen. Manchmal sind Standardwerte durchaus vernünftig und keine schlechte Praxis.In Java 7 gab es einen Vorschlag dafür, der jedoch abgelehnt wurde:
http://tech.puredanger.com/java7/#null
quelle
Ein Weg, um das Fehlen von "?" Der Operator, der Java 8 ohne den Aufwand von try-catch verwendet (der
NullPointerException
, wie erwähnt, auch einen Ursprung an anderer Stelle verbergen könnte ), erstellt eine Klasse für "Pipe" -Methoden in einem Java-8-Stream-Stil.public class Pipe<T> { private T object; private Pipe(T t) { object = t; } public static<T> Pipe<T> of(T t) { return new Pipe<>(t); } public <S> Pipe<S> after(Function<? super T, ? extends S> plumber) { return new Pipe<>(object == null ? null : plumber.apply(object)); } public T get() { return object; } public T orElse(T other) { return object == null ? other : object; } }
Dann würde das gegebene Beispiel werden:
public String getFirstName(Person person) { return Pipe.of(person).after(Person::getName).after(Name::getGivenName).get(); }
[BEARBEITEN]
Bei weiteren Überlegungen stellte ich fest, dass es tatsächlich möglich ist, dasselbe nur mit Standard-Java 8-Klassen zu erreichen:
public String getFirstName(Person person) { return Optional.ofNullable(person).map(Person::getName).map(Name::getGivenName).orElse(null); }
In diesem Fall ist es sogar möglich, einen Standardwert (wie
"<no first name>"
) auszuwählen, anstattnull
ihn als Parameter von zu übergebenorElse
.quelle
Pipe
Klasse verbessern , um eineorElse
Funktionalität so anzupassen , dass ich ein beliebiges Nicht-Null-Objekt innerhalb derorElse
Methode übergeben kann?orElse
Methode zurPipe
Klasse hinzugefügt .Siehe: https://blogs.oracle.com/darcy/project-coin:-the-final-five-or-so (speziell "Elvis und andere null sichere Operatoren").
Das Ergebnis ist, dass diese Funktion für Java 7 in Betracht gezogen wurde, aber nicht enthalten war.
quelle
Das ist eigentlich Groovys Safe-Dereferenzierungs-Operator . Sie können es nicht in reinem Java verwenden (leider), so dass dieser Beitrag einfach falsch ist (oder eher leicht irreführend, wenn behauptet wird, Groovy sei die "neueste Version von Java").
quelle
Java hat nicht die genaue Syntax, aber ab JDK-8 verfügen wir über die optionale API mit verschiedenen Methoden. Also die C # -Version unter Verwendung des bedingten Nulloperators :
return person?.getName()?.getGivenName();
kann in Java mit der optionalen API wie folgt geschrieben werden :
return Optional.ofNullable(person) .map(e -> e.getName()) .map(e -> e.getGivenName()) .orElse(null);
Wenn einer von oder null ist
person
, wird null zurückgegeben.getName
getGivenName
quelle
Es ist möglich, util-Methoden zu definieren, die dies mit Java 8 Lambda auf fast hübsche Weise lösen.
Dies ist eine Variante der H-MAN-Lösung , verwendet jedoch überladene Methoden mit mehreren Argumenten, um mehrere Schritte zu verarbeiten, anstatt sie abzufangen
NullPointerException
.Selbst wenn ich denke, dass diese Lösung irgendwie cool ist, bevorzuge ich Helder Pereiras zweite, da dies keine nützlichen Methoden erfordert.
void example() { Entry entry = new Entry(); // This is the same as H-MANs solution Person person = getNullsafe(entry, e -> e.getPerson()); // Get object in several steps String givenName = getNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.getGivenName()); // Call void methods doNullsafe(entry, e -> e.getPerson(), p -> p.getName(), n -> n.nameIt()); } /** Return result of call to f1 with o1 if it is non-null, otherwise return null. */ public static <R, T1> R getNullsafe(T1 o1, Function<T1, R> f1) { if (o1 != null) return f1.apply(o1); return null; } public static <R, T0, T1> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, R> f2) { return getNullsafe(getNullsafe(o0, f1), f2); } public static <R, T0, T1, T2> R getNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Function<T2, R> f3) { return getNullsafe(getNullsafe(o0, f1, f2), f3); } /** Call consumer f1 with o1 if it is non-null, otherwise do nothing. */ public static <T1> void doNullsafe(T1 o1, Consumer<T1> f1) { if (o1 != null) f1.accept(o1); } public static <T0, T1> void doNullsafe(T0 o0, Function<T0, T1> f1, Consumer<T1> f2) { doNullsafe(getNullsafe(o0, f1), f2); } public static <T0, T1, T2> void doNullsafe(T0 o0, Function<T0, T1> f1, Function<T1, T2> f2, Consumer<T2> f3) { doNullsafe(getNullsafe(o0, f1, f2), f3); } class Entry { Person getPerson() { return null; } } class Person { Name getName() { return null; } } class Name { void nameIt() {} String getGivenName() { return null; } }
quelle
Ich bin mir nicht sicher, ob das überhaupt funktionieren würde. Wenn beispielsweise die Personenreferenz null wäre, durch was würde die Laufzeit sie ersetzen? Eine neue Person? Dies würde erfordern, dass die Person über eine Standardinitialisierung verfügt, die Sie in diesem Fall erwarten würden. Sie können Nullreferenzausnahmen vermeiden, aber Sie würden immer noch ein unvorhersehbares Verhalten erhalten, wenn Sie diese Art von Setups nicht geplant hätten.
Das ?? Der Operator in C # kann am besten als "Koaleszenz" -Operator bezeichnet werden. Sie können mehrere Ausdrücke verketten und es wird der erste zurückgegeben, der nicht null ist. Java hat es leider nicht. Ich denke, das Beste, was Sie tun können, ist, den ternären Operator zu verwenden, um Nullprüfungen durchzuführen und eine Alternative zum gesamten Ausdruck zu bewerten, wenn ein Mitglied in der Kette null ist:
return person == null ? "" : person.getName() == null ? "" : person.getName().getGivenName();
Sie können auch try-catch verwenden:
try { return person.getName().getGivenName(); } catch(NullReferenceException) { return ""; }
quelle
getName()
odergetGivenName()
die Sie nicht wissen , ob Sie einfach eine leere Zeichenfolge für alle Vorkommen zurück.person?.getName()?.getGivenName() ?? ""
ist das Äquivalent Ihres ersten Beispiels, mit der Ausnahme, dass wenngetGivenName()
null zurückgegeben wird, es immer noch gibt""
Da haben Sie es, null-sicherer Aufruf in Java 8:
public void someMethod() { String userName = nullIfAbsent(new Order(), t -> t.getAccount().getUser() .getName()); } static <T, R> R nullIfAbsent(T t, Function<T, R> funct) { try { return funct.apply(t); } catch (NullPointerException e) { return null; } }
quelle
Wenn jemand nach einer Alternative für alte Java-Versionen sucht, können Sie diese ausprobieren, die ich geschrieben habe:
/** * Strong typed Lambda to return NULL or DEFAULT VALUES instead of runtime errors. * if you override the defaultValue method, if the execution result was null it will be used in place * * * Sample: * * It won't throw a NullPointerException but null. * <pre> * {@code * new RuntimeExceptionHandlerLambda<String> () { * @Override * public String evaluate() { * String x = null; * return x.trim(); * } * }.get(); * } * <pre> * * * @author Robson_Farias * */ public abstract class RuntimeExceptionHandlerLambda<T> { private T result; private RuntimeException exception; public abstract T evaluate(); public RuntimeException getException() { return exception; } public boolean hasException() { return exception != null; } public T defaultValue() { return result; } public T get() { try { result = evaluate(); } catch (RuntimeException runtimeException) { exception = runtimeException; } return result == null ? defaultValue() : result; } }
quelle
Sie können den von Ihnen bereitgestellten Code testen und es wird ein Syntaxfehler ausgegeben. Daher wird er in Java nicht unterstützt. Groovy unterstützt es und es wurde für Java 7 vorgeschlagen (wurde aber nie aufgenommen).
Sie können jedoch die in Java 8 bereitgestellte Option verwenden. Dies kann Ihnen dabei helfen, etwas in einer ähnlichen Zeile zu erreichen. https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html
Beispielcode für Optional
quelle
Da Android Lambda-Funktionen nur unterstützt, wenn Ihr installiertes Betriebssystem> = 24 ist, müssen wir Reflection verwenden.
// Example using doIt function with sample classes public void Test() { testEntry(new Entry(null)); testEntry(new Entry(new Person(new Name("Bob")))); } static void testEntry(Entry entry) { doIt(doIt(doIt(entry, "getPerson"), "getName"), "getName"); } // Helper to safely execute function public static <T,R> R doIt(T obj, String methodName) { try { if (obj != null) return (R)obj.getClass().getDeclaredMethod(methodName).invoke(obj); } catch (Exception ignore) { } return null; }
// Sample test classes static class Entry { Person person; Entry(Person person) { this.person = person; } Person getPerson() { return person; } } static class Person { Name name; Person(Name name) { this.name = name; } Name getName() { return name; } } static class Name { String name; Name(String name) { this.name = name; } String getName() { System.out.print(" Name:" + name + " "); return name; } } }
quelle
Wenn dies für Sie kein Leistungsproblem ist, können Sie schreiben
public String getFirstName(Person person) { try { return person.getName().getGivenName(); } catch (NullPointerException ignored) { return null; } }
quelle
getName()
Zum Beispiel, wenn intern eine Ausnahme ausgelöst wurde, die Sie nicht wegwerfen möchten.Person
könnte ein Proxy auf eine Datenbank oder einen Nicht-Heap-Speicher zugreifen, und irgendwo könnte ein Fehler auftreten ... Nicht sehr realistisch, aber Peter, ich wette, Sie haben noch nie einen Code wie oben geschrieben .