2010-12-08 4 views
59

ich einen Artikel aus einer slashdot Geschichte verbunden war zu lesen, und kam in diesen kleinen Leckerbissen:Java "?" Operator zum Überprüfen der Null - Was ist das? (Nicht Ternary!)

Nehmen Sie die neueste Version von Java, die versucht Null-Pointer-Überprüfung leichter zu machen, indem bietet Kurzschrift-Syntax für die Endlos-Zeiger-Tests. gerade hinzufügen, ein Fragezeichen zu jeder Methode Aufruf automatisch einen Test für Null-Zeiger enthält, ein Rattennest von if-then-Anweisungen ersetzt, so wie:

 
    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 durchforstet (okay, ich verbrachte mindestens 15 Minuten Googlen Variationen über "Java Fragezeichen") und bekam nichts. Also, meine Frage: Gibt es eine offizielle Dokumentation? Ich habe festgestellt, dass C# einen ähnlichen Operator hat (den Operator "??"), aber ich möchte die Dokumentation für die Sprache, in der ich arbeite, erhalten. Oder ist das nur eine Verwendung des ternären Operators, den ich habe noch nie vorher gesehen.

Danke!

EDIT: Link zum Artikel: http://infoworld.com/d/developer-world/12-programming-mistakes-avoid-292

+3

Könnten wir einen Link zu dem Artikel zumindest? –

+0

Und die Quelle des Snippets? – khachik

+5

Der Artikel ist falsch. http://infoworld.com/print/145292 Ich glaube, dass eine Projekt Coin Einreichung dafür eingereicht wurde. Aber es wurde nicht ausgewählt (aus Gründen in dem Artikel erwähnt - wenn Sie so etwas tun möchten, verwenden Sie C# oder etwas), und ist sicherlich nicht in der aktuellen Version der Java-Sprache. –

Antwort

60

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 aber noch nicht akzeptiert.

Der verwandte Elvis-Operator?: Wurde vorgeschlagen, x ?: y shorthand für x != null ? x : y, besonders nützlich, wenn x ein komplexer Ausdruck ist.

+5

"Elvis"? Ich sehe es nicht ... –

+2

In Java (wo es keine automatische Nullung gibt) eine Kurzschrift für 'x! = Null? x: y' –

+11

@Karl: Drehen Sie es um 90 Grad, wie ein ':-)' Smilie. –

45

Diese Syntax existiert nicht in Java, noch ist es in allen kommenden Versionen aufgenommen wird geplant, die ich kenne.

+3

Warum der Downvote? Diese Antwort ist zu 100% richtig, soweit ich weiß ... wenn Sie etwas anderes wissen, sagen Sie es bitte. – ColinD

+0

aber offensichtlich zu einem Zeitpunkt wurde es eingereicht (siehe Tom Hawtin Kommentare) und wird es vielleicht in die Sprache schaffen. Also ein * "Diese Syntax existiert ** noch nicht ** in Java" * wäre besser geeignet. – SyntaxT3rr0r

+6

@Webinator: Es wird nicht in Java 7 oder 8 sein, und es gibt keine anderen "kommenden Versionen" zu diesem Zeitpunkt. Ich finde es auch ziemlich unwahrscheinlich, dass es dazu kommen wird, da es eher schlechte Praktiken fördert. Ich glaube auch nicht, dass "noch" notwendig ist, da "in Java nicht existiert" nicht dasselbe ist wie "wird niemals in Java existieren". – ColinD

6

Das ist eigentlich Groovy's safe-dereference operator. Sie können es nicht in reinem Java verwenden (leider), so dass Post einfach falsch ist (oder eher leicht irreführend ist, wenn behauptet wird, dass Groovy die "neueste Version von Java" ist).

+1

Also der Artikel war falsch - die Syntax existiert nicht in nativem Java. Hmm. –

+0

aber die Verbindung ist unterbrochen – bvdb

1

Ich bin mir nicht sicher, das würde sogar funktionieren; Wenn, sagen wir mal, die Personenreferenz NULL wäre, womit würde die Laufzeit sie ersetzen? Eine neue Person? Das würde erfordern, dass die Person eine Standardinitialisierung hat, die Sie in diesem Fall erwarten würden. Sie können Null-Referenz-Ausnahmen vermeiden, aber Sie würden immer noch unvorhersehbares Verhalten bekommen, wenn Sie diese Art von Setup nicht geplant haben.

Die ?? Der Operator in C# könnte am besten als Koaleszenzoperator bezeichnet werden. Sie können mehrere Ausdrücke verketten und es wird das erste zurückgegeben, das nicht null ist. Leider hat Java es nicht.Ich denke, das Beste, was Sie tun könnten, um den ternären Operator verwenden, um null Prüfungen durchführt und eine Alternative zu dem gesamten Ausdruck zu bewerten, wenn jedes Mitglied in der Kette ist null:

return person == null ? "" 
    : person.getName() == null ? "" 
     : person.getName().getGivenName(); 

Sie auch versuchen, Beifang nutzen könnten:

try 
{ 
    return person.getName().getGivenName(); 
} 
catch(NullReferenceException) 
{ 
    return ""; 
} 
+1

"Was würde die Laufzeit durch ersetzen?" ... das Lesen der Frage könnte helfen :-P Es würde es durch Null ersetzen. Im Allgemeinen scheint die Idee zu sein, dass diese Person? .getName null ergibt, wenn die Person null ist, oder person.getName, falls nicht. Es ist also so, als würde man in allen Beispielen "" durch null ersetzen. – subsub

+1

Eine NullReferenceException konnte auch in 'getName()' oder 'getGivenName()' geworfen werden, was Sie nicht wissen, wenn Sie einfach eine leere Zeichenfolge für alle Vorkommnisse zurückgeben. –

+1

In Java ist es NullPointerException. – Tuupertunut

-3

Wenn dies nicht für Sie ein Performance-Problem ist, können Sie

public String getFirstName(Person person) { 
    try { 
    return person.getName().getGivenName(); 
    } catch (NullPointerException ignored) { 
    return null; 
    } 
} 
+7

Das Problem mit diesem Stil ist, dass die NullPointerException möglicherweise nicht von der erwarteten Stelle gekommen ist es zu. Und so kann es einen echten Fehler verbergen. – Darron

+2

@Darron, Können Sie ein Beispiel für einen Getter geben, den Sie geschrieben haben, der eine NPE werfen könnte und wie Sie anders damit umgehen würden? –

+2

Sie führen es in eine separate Variable und testen es mit einem if als normal. Die Idee hinter diesem Operator ist es, diese Hässlichkeit zu beseitigen. Darron hat Recht, deine Lösung könnte Ausnahmen verstecken und wegwerfen, die du werfen willst. Zum Beispiel, wenn 'getName()' intern eine Ausnahme auslöst, die Sie nicht wegwerfen wollen. –

0

Da haben Sie es, null sicheren Aufruf in Java 8 schreiben:

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; 
    } 
} 
+0

Ich werde das versuchen müssen. Ich habe ernsthafte Zweifel an diesem ganzen "optionalen" Geschäft. Scheint wie ein hässlicher Hack. – ggb667

+3

Der gesamte Zweck des Elvis-Operator-Vorschlags bestand darin, dies in einer Zeile zu tun. Dieser Ansatz ist nicht besser als der "if (! = Null) Ansatz oben. In der Tat würde ich argumentieren, dass es schlimmer ist, da es keine direkte ist. Darüber hinaus sollten Sie vermeiden, werfen und fangen Fehler aufgrund der Overhead. – JustinKSU

+0

Als @Darron sagte in einer anderen Antwort, das gleiche gilt hier: "Das Problem mit diesem Stil ist, dass die NullPointerException möglicherweise nicht von wo Sie es erwartet haben. Und so kann es einen echten Fehler verbergen. " –

12

Ein Weg zur Problemumgehung das Fehlen von "?" Der Operator, der Java 8 ohne den Overhead von try-catch verwendet (der auch eine NullPointerException, die anderswo entstanden ist, wie erwähnt), kann eine Klasse erstellen, die Methoden in einem Java-8-Stream-Stil "pipe".

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 wird das gegebene Beispiel würde:

public String getFirstName(Person person) { 
    return Pipe.of(person).after(Person::getName).after(Name::getGivenName).get(); 
} 

[EDIT]

Beim weiteren Gedanken, habe ich herausgefunden, dass es tatsächlich möglich ist, das gleiche zu erreichen nur unter Verwendung von Standard Java 8 Klassen:

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>") anstelle von null zu wählen, indem Sie ihn als Parameter von orElse übergeben.

+0

Ich mag Ihre erste Lösung besser. Wie würden Sie Ihre' Pipe'-Klasse verbessern, um eine 'orElse'-Funktionalität anzupassen, so dass ich ein beliebiges Nicht-Null-Objekt innerhalb des' 'übergeben könnte orElse' Methode? – ThanosFisherman

+0

@ThanosFisherman Ich habe die 'orElse' Methode zur' Pipe' Klasse hinzugefügt –

2

Es ist möglich, util-Methoden zu definieren, die dies auf fast hübsche Weise mit Java 8 lambda lösen.

Dies ist eine Variation von H-MANs solution, aber es verwendet überladene Methoden mit mehreren Argumenten, um mehrere Schritte zu behandeln, anstatt NullPointerException zu fangen.

Auch wenn ich denke, diese Lösung ist irgendwie cool, denke ich bevorzuge ich Helder Pereira's Sekunden, da das keine util 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; } 
} 
0

Wenn jemand eine Alternative für alte Java-Versionen suchen, können Sie versuchen diese schrieb ich:

/** 
* 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; 
    } 

} 
0

Sie können den Code testen, die Sie zur Verfügung gestellt haben, und es wird Syntaxfehler geben. Daher wird es 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 Option in Java 8 zur Verfügung gestellt. Dies könnte Ihnen helfen, etwas auf einer ähnlichen Linie 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

Example Code for Optional

+0

Während dieser Link die Frage beantworten kann, ist es besser, die wesentlichen Teile der Antwort hier einzubeziehen und den Link als Referenz zur Verfügung zu stellen. Link-Only-Antworten können ungültig werden, wenn sich die verknüpfte Seite ändert. - [Aus Bewertung] (/ review/low-quality-posts/18103763) –

Verwandte Themen