Ява "?" Оператор для проверки 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
Ответы
Ответ 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, и он не будет включен в любую из будущих версий, о которых я знаю.
Ответ 3
В Java 7 было предложение, но оно было отклонено:
http://tech.puredanger.com/java7/#null
Ответ 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;
}
}