Я читал статью, на которую ссылается история со слэшдотом, и наткнулся на этот небольшой лакомый кусочек:
Возьмем последнюю версию 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 минут на поиск в Google вариантов "вопросительного знака java") и ничего не нашел. Итак, мой вопрос: есть ли на это официальная документация? Я обнаружил, что в C # есть аналогичный оператор (оператор "??"), но я хотел бы получить документацию для языка, на котором я работаю. Или это просто использование тернарного оператора, который у меня никогда раньше не видел.
Благодарность!
РЕДАКТИРОВАТЬ: ссылка на статью: http://infoworld.com/d/developer-world/12-programming-mistakes-avoid-292
A ?? B == (A != null) ? A : B
. Это , как представляется , оценить свойство на объекте , если ссылка на объект не является нулевым, то естьA?.B == (A != null) ? A.B : null
.Ответы:
Первоначальная идея пришла от groovy. Он был предложен для Java 7 как часть Project Coin: https://wiki.openjdk.java.net/display/Coin/2009+Proposals+TOC (Elvis и другие Null-Safe Operators), но еще не был принят .
Связанный оператор Элвиса?: Был предложен для
x ?: y
сокращенияx != null ? x : y
, особенно полезного, когда x - сложное выражение.источник
x!=null ? x : y
?
фирменная причёска Элвиса Пресли;:
просто представляет собой пару глаз , как обычно. Возможно?:-o
, более запоминающийся ...?:0
Должен быть оператором «Ни нуль, ни 0». Сделай это так.Этого синтаксиса нет в Java, и его не планируется включать ни в одну из будущих версий, о которых я знаю.
источник
Optional
иmap
прочее. мы не скрываем проблему, если value допускает значение NULL, что означает, что иногда ожидается, что оно будет равно NULL, и вам нужно с этим справиться. иногда значения по умолчанию вполне разумны, и это неплохая практика.Было предложение об этом в Java 7, но оно было отклонено:
http://tech.puredanger.com/java7/#null
источник
Один из способов обойти отсутствие символа "?" Оператор с использованием 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
.источник
Pipe
класс, чтобы адаптироватьorElse
функциональность, чтобы я мог передавать произвольный ненулевой объект внутриorElse
метода?orElse
метод вPipe
класс.См. Https://blogs.oracle.com/darcy/project-coin:-the-final-five-or-so (в частности, «Элвис и другие операторы с нулевой безопасностью»).
В результате эта функция рассматривалась для Java 7, но не была включена.
источник
На самом деле это оператор безопасного разыменования Groovy . Вы не можете использовать его на чистой Java (к сожалению), поэтому этот пост просто неверен (или, что более вероятно, слегка вводит в заблуждение, если он утверждает, что Groovy является «последней версией Java»).
источник
В Java нет точного синтаксиса, но, начиная с JDK-8, в нашем распоряжении есть дополнительный API с различными методами. Итак, версия C # с использованием условного оператора null :
return person?.getName()?.getGivenName();
можно записать на Java с помощью Optional API следующим образом :
return Optional.ofNullable(person) .map(e -> e.getName()) .map(e -> e.getGivenName()) .orElse(null);
если любой из
person
,getName
илиgetGivenName
в нуль , то нуль возвращается.источник
С помощью лямбда-выражения Java 8 можно определить методы утилиты, которые решают эту проблему почти красиво.
Это вариант решения H-MAN, но он использует перегруженные методы с несколькими аргументами для обработки нескольких шагов вместо перехвата
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; } }
источник
Я не уверен, что это вообще сработает; если, скажем, ссылка на человека была пустой, чем среда выполнения заменила бы ее? Новый человек? Это потребует от Person инициализации по умолчанию, которую вы ожидаете в этом случае. Вы можете избежать исключений с нулевой ссылкой, но вы все равно получите непредсказуемое поведение, если не планируете эти типы настроек.
?? Оператор в C # лучше всего назвать оператором «объединения»; вы можете связать несколько выражений, и оно вернет первое, которое не равно нулю. К сожалению, в Java этого нет. Я думаю, что лучшее, что вы могли бы сделать, - это использовать тернарный оператор для выполнения нулевых проверок и оценки альтернативы всему выражению, если какой-либо член в цепочке имеет значение NULL:
return person == null ? "" : person.getName() == null ? "" : person.getName().getGivenName();
Вы также можете использовать try-catch:
try { return person.getName().getGivenName(); } catch(NullReferenceException) { return ""; }
источник
getName()
илиgetGivenName()
которые вы не будете знать , если вы просто возвращаете пустую строку для всех случаев.person?.getName()?.getGivenName() ?? ""
является эквивалентом вашего первого примера, за исключением того, что еслиgetGivenName()
возвращает null, он все равно выдаст""
Вот он, нулевой безопасный вызов в 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; } }
источник
Если кто-то ищет альтернативу старым версиям 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; } }
источник
Вы можете протестировать предоставленный код, и он выдаст синтаксическую ошибку, поэтому он не поддерживается в Java. Groovy поддерживает его, и он был предложен для Java 7 (но так и не был включен).
Однако вы можете использовать опцию Optional, предусмотренную в 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
Пример кода для необязательного
источник
Поскольку 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; } } }
источник
Если для вас это не проблема производительности, вы можете написать
public String getFirstName(Person person) { try { return person.getName().getGivenName(); } catch (NullPointerException ignored) { return null; } }
источник
getName()
вы внутренне выбросили исключение, которое вы не хотите выбрасывать.Person
может быть прокси-сервер, обращающийся к БД или некоторой памяти, отличной от кучи, и где-то может быть ошибка ... Не очень реалистично, но, Питер, держу пари, вы никогда не писали кусок кода, как указано выше .