Java"?"Operator do sprawdzania null - Co to jest? (Nie Ternary!)

Czytałam artykuł związany z historią slashdota i natknęłam się na taki mały drobiazg:

Weź najnowszą wersję Javy, która próbuje sprawdzić wskaźnik null łatwiejsze, oferując skróconą składnię do niekończących się testów wskaźników. Just dodawanie znaku zapytania do każdej metody wywołanie automatycznie zawiera test na wskaźniki null, zastępując szczurzego gniazda wypowiedzi if-then, takich jako: {[0]}

Z tym: {[1]}

Przeszukałem internet (ok, spędziłem co najmniej 15 minut googlując wariacje na temat "java question mark") i nic nie znalazłem. Moje pytanie: czy jest jakaś oficjalna dokumentacja na ten temat? Odkryłem, że C# ma podobny operator ("??"operator), ale chciałbym uzyskać dokumentację dla języka, w którym pracuję. Czy jest to tylko użycie operatora trójdzielnego, którego nigdy wcześniej nie widziałem.

Dzięki!

EDIT: Link do artykułu: http://infoworld.com/d/developer-world/12-programming-mistakes-avoid-292

Author: Erty Seidohl, 2010-12-08

13 answers

Oryginalny pomysł pochodzi z groovy. Został on zaproponowany dla Javy 7 jako część projektu Coin: https://wiki.openjdk.java.net/display/Coin/2009 + Proposals + TOC (Elvis i inni operatorzy Null-Safe), ale nie została jeszcze przyjęta.

/ Align = "left" /: zaproponowano, aby x ?: y było skrótem x != null ? x : y, szczególnie użytecznym, gdy X jest złożonym wyrażeniem.
 65
Author: ataylor,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-01-07 20:02:41

Ta składnia nie istnieje w Javie, ani nie ma być dołączona do żadnej z nadchodzących wersji, o których wiem.

 48
Author: ColinD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-08 17:09:01

W Javie 7 pojawiła się propozycja, ale została odrzucona:

Http://tech.puredanger.com/java7/#null

 16
Author: PaulJWilliams,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-08 17:06:33

Jeden sposób na obejście braku"?"operatorem używającym Javy 8 bez narzutu try-catch (który mógłby również ukryć NullPointerException pochodzące z innego miejsca, jak wspomniano) jest stworzenie klasy do" rurowania " metod w stylu Java-8-Stream.

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

Wtedy podany przykład będzie:

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

[EDIT]

Po zastanowieniu się, doszedłem do wniosku, że możliwe jest osiągnięcie tego samego tylko przy użyciu standardowych klas Java 8:

public String getFirstName(Person person) {
    return Optional.ofNullable(person).map(Person::getName).map(Name::getGivenName).orElse(null);
}

W tym przypadku, można nawet wybrać wartość domyślną (np. "<no first name>") zamiast null, przekazując ją jako parametr orElse.

 13
Author: Helder Pereira,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-06-20 06:56:47

Zobacz: https://blogs.oracle.com/darcy/project-coin:-the-final-five-or-so (w szczególności "Elvis and other null safe operators").

Wynik jest taki, że ta funkcja była brana pod uwagę w Javie 7, ale nie została uwzględniona.

 7
Author: Darron,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-09 21:44:12

To właściwie Operator bezpiecznej dereferencji Groovy ' ego . Nie można go używać w czystej Javie (niestety), więc ten post jest po prostu błędny (lub raczej nieco mylący, jeśli twierdzi, że Groovy to "najnowsza wersja Javy").

 6
Author: Andrzej Doyle,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-09 21:58:59

Możliwe jest zdefiniowanie metod util, które rozwiązują to w prawie ładny sposób za pomocą Java 8 lambda.

Jest to odmiana rozwiązania H-MANs , ale używa przeciążonych metod z wieloma argumentami do obsługi wielu kroków zamiast przechwytywania NullPointerException.

Nawet jeśli myślę, że to rozwiązanie jest fajne, myślę, że wolę Helder Pereira ' s seconds one, ponieważ nie wymaga to żadnych metod util.

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; }
}
 2
Author: Lii,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-17 17:35:55

Nie jestem pewien, czy to w ogóle zadziała; jeśli, powiedzmy, Referencja do osoby była null, co by ją zastąpiło przez runtime? Nowa osoba? Wymagałoby to od tej osoby domyślnej inicjalizacji, której można by się spodziewać w tym przypadku. Możesz unikać WYJĄTKÓW null reference, ale nadal zachowywałbyś się nieprzewidywalnie, gdybyś nie planował tego typu konfiguracji.

The ?? operator w C# może być najlepiej określany operatorem "coalesce"; można łańcuchować kilka wyrażeń i zwróci po pierwsze, to nie jest null. Niestety Java tego nie ma. Myślę, że najlepsze co możesz zrobić, to użyć operatora trójdzielnego do wykonania kontroli null i oceny alternatywy dla całego wyrażenia, jeśli jakikolwiek element w łańcuchu jest null:

return person == null ? "" 
    : person.getName() == null ? "" 
        : person.getName().getGivenName();
Możesz też użyć try-catch:
try
{
   return person.getName().getGivenName();
}
catch(NullReferenceException)
{
   return "";
}
 1
Author: KeithS,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-22 18:57:15

Tutaj masz, null-bezpieczne wywołanie w Javie 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;
    }
}
 0
Author: H-MAN,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2014-03-20 20:49:35

Jeśli ktoś szuka alternatywy dla starych wersji Javy, może spróbować tej, którą napisałem:

/**
 * 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
Author: irobson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-11-18 20:49:05

Możesz przetestować kod, który podałeś i poda składnię error.So, nie jest obsługiwana w Javie. Groovy nie obsługuje go i został zaproponowany dla Java 7 (ale nigdy nie został uwzględniony).

Można jednak użyć opcjonalnej wersji Java 8. Może to pomóc w osiągnięciu czegoś podobnego Kolejka https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

Przykładowy kod dla opcjonalnego

 0
Author: SK -,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2017-11-29 18:55:26

Java nie posiada dokładnej składni , ale od JDK-8 mamy opcjonalne API z różnymi metodami do dyspozycji. Tak więc, Wersja C# z użyciem null operator warunkowy :

return person?.getName()?.getGivenName(); 

Można zapisać w Javie w następujący sposób za pomocą opcjonalnego API :

 return Optional.ofNullable(person)
                .map(e -> e.getName())
                .map(e -> e.getGivenName())
                .orElse(null);

Jeśli którykolwiek z person, getName or getGivenName is null then null is returned.

 0
Author: Aomine,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-10 15:17:15

Jeśli nie jest to dla Ciebie problem z wydajnością, możesz napisać

public String getFirstName(Person person) {
  try {
     return person.getName().getGivenName();
  } catch (NullPointerException ignored) {
     return null;
  }
} 
 -3
Author: Peter Lawrey,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2010-12-08 17:48:19