Ява "?" Оператор для проверки null - что это такое? (Не трогательно!) - программирование

Ява "?" Оператор для проверки null - что это такое? (Не трогательно!)

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

Возьмите последнюю версию 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 question mark" ) и ничего не получил. Итак, мой вопрос: есть ли официальная документация по этому поводу? Я обнаружил, что у С# есть аналогичный оператор (оператор "??" ), но я хотел бы получить документацию для языка, в котором я работаю. Или это просто использование тернарного оператора, который я никогда не видел раньше.

Спасибо!

EDIT: Ссылка на статью: http://infoworld.com/d/developer-world/12-programming-mistakes-avoid-292

4b9b3361

Ответ 1

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

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

Ответ 2

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

Ответ 4

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

[EDIT]

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

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

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

Ответ 5

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

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

Ответ 6

Фактически Groovy оператор безопасного разыменования. Вы не можете использовать его в чистой Java (к сожалению), так что сообщение просто неверно (или, скорее, слегка вводит в заблуждение, если оно требует, чтобы Groovy была "последней версией Java" ).

Ответ 7

Можно определить методы использования, которые разрешают это почти красиво с помощью Java 8 lambda.

Это вариант 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; }
}

Ответ 8

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

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

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

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

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

Ответ 9

Там у вас есть нулевой безопасный вызов в 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;
    }
}

Ответ 10

Если кто-то ищет альтернативу для старых java-версий, вы можете попробовать этот, который я написал:

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

}

Ответ 11

Вы можете протестировать код, который вы предоставили, и он даст синтаксическую ошибку. Поэтому он не поддерживается в Java. Groovy поддерживает его, и он был предложен для Java 7 (но так и не был включен).

Однако вы можете использовать опцию, предоставленную в Java 8. Это может помочь вам в достижении чего-то подобного уровня. https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html

Пример кода для необязательного

Ответ 12

Если это не проблема производительности, вы можете написать

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