Джава "?"Оператор для проверки нуля - что это?(Не троичный!) - PullRequest
68 голосов
/ 08 декабря 2010

Я читал статью, связанную с историей о слэшдоте, и наткнулся на этот маленький кусочек:

Возьмите последнюю версию Java, которая пытается выполнить проверку нулевого указателя проще, предлагая сокращенный синтаксис для бесконечного тестирования указателя. Просто добавление знака вопроса к каждому методу вызов автоматически включает в себя проверить нулевые указатели, заменив крысиное гнездо утверждений if-then, таких как: public String getPostcode(Person person) { String ans= null; if (person != null) { Name nm= person.getName(); if (nm!= null) { ans= nm.getPostcode(); } } return ans }

С этим: public String getFirstName(Person person) { return person?.getName()?.getGivenName(); }

Я искал в интернете (хорошо, я потратил не менее 15 минут на поиски вариантов «знака вопроса java») и ничего не получил. Итак, мой вопрос: есть ли официальная документация по этому вопросу? Я обнаружил, что в C # есть аналогичный оператор (оператор «??»), но я хотел бы получить документацию для языка, на котором я работаю. Или это просто использование троичного оператора, который я использую? никогда не видел раньше.

Спасибо!

РЕДАКТИРОВАТЬ: Ссылка на статью: http://infoworld.com/d/developer-world/12-programming-mistakes-avoid-292

Ответы [ 14 ]

68 голосов
/ 08 декабря 2010

Оригинальная идея происходит от заводной. Он был предложен для Java 7 как часть Project Coin: https://wiki.openjdk.java.net/display/Coin/2009+Proposals+TOC (Элвис и другие нуль-безопасные операторы), но пока не принят.

Связанный оператор Элвиса?: Было предложено сделать x ?: y сокращение для x != null ? x : y, особенно полезно, когда x является сложным выражением.

53 голосов
/ 08 декабря 2010

Этот синтаксис не существует в Java и не планируется включать ни в одну из следующих известных мне версий.

16 голосов
/ 08 декабря 2010

Было предложение об этом в Java 7, но оно было отклонено:

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

15 голосов
/ 02 августа 2015

Один из способов обойти отсутствие "?"Оператор, использующий Java 8 без издержек try-catch (который также может скрыть NullPointerException, созданный в другом месте, как уже упоминалось), должен создать класс для "конвейерного" метода в стиле 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;
    }
}

Тогда приведенный пример станет:

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

[РЕДАКТИРОВАТЬ]

После дальнейших размышлений я понял, что это на самом делеможно добиться того же, только используя стандартные классы Java 8:

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

В этом случае даже можно выбрать значение по умолчанию (например, "<no first name>") вместо null, передав его в качестве параметраorElse.

7 голосов
/ 08 декабря 2010

См .: https://blogs.oracle.com/darcy/project-coin:-the-final-five-or-so (в частности, «Элвис и другие нулевые безопасные операторы»).

В результате эта функция рассматривалась для Java 7, но не была включена.

6 голосов
/ 08 декабря 2010

Это на самом деле Оператор безопасного разыменования Groovy . Вы не можете использовать его в чистой Java (к сожалению), так что публикация просто неправильна (или, скорее, слегка вводит в заблуждение, если заявляет, что Groovy - «последняя версия Java»).

2 голосов
/ 14 ноября 2015

Можно определить методы util, которые решают это почти симпатичным способом с лямбдой Java 8.

Это вариант решения H-MANs , но он использует перегруженные методы снесколько аргументов для обработки нескольких шагов вместо перехвата NullPointerException.

Даже если я думаю, что это решение довольно круто, я думаю, что я предпочитаю секунд Хелдера Перейры, так как для этого не требуется никакогоутилитарные методы.

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; }
}
1 голос
/ 10 июня 2018

В Java нет синтаксиса точного , но по состоянию на JDK-8 у нас есть Дополнительный API с различными методами в нашем распоряжении Итак, версия C # с использованием условного оператора null :

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

может быть написано следующим образом на Java с помощью Необязательный API :

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

если любой из person, getName или getGivenName равен нулю, возвращается ноль.

1 голос
/ 08 декабря 2010

Я не уверен, что это сработает;скажем, если ссылка на человека была нулевой, чем бы заменила среда выполнения?Новый Человек?Это потребовало бы, чтобы Человек имел некоторую инициализацию по умолчанию, которую вы ожидаете в этом случае.Вы можете избегать исключений с нулевыми ссылками, но вы все равно получите непредсказуемое поведение, если не планируете установки такого типа.

The ??оператор в C # лучше всего назвать оператором "coalesce";Вы можете объединить несколько выражений в цепочку, и оно вернет первое, которое не равно нулю.К сожалению, у Java этого нет.Я думаю, что лучшее, что вы могли бы сделать, это использовать троичный оператор для выполнения нулевых проверок и оценки альтернативы всему выражению, если какой-либо элемент в цепочке равен нулю:

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

Вы также можете использовать try-catch:

try
{
   return person.getName().getGivenName();
}
catch(NullReferenceException)
{
   return "";
}
0 голосов
/ 30 мая 2019

Поскольку Android не поддерживает лямбда-функции, если у вас не установлена ​​ОС> = 24, нам нужно использовать отражение.

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