Eu estava lendo um artigo com link de uma história do slashdot e me deparei com este pequeno boato:
Pegue a versão mais recente do Java, que tenta tornar a verificação de ponteiro nulo mais fácil, oferecendo uma sintaxe abreviada para o teste de ponteiro sem fim. Apenas adicionar um ponto de interrogação a cada invocação de método inclui automaticamente um teste para ponteiros nulos, substituindo um ninho de rato de instruções if-then, como:
public String getPostcode(Person person) { String ans= null; if (person != null) { Name nm= person.getName(); if (nm!= null) { ans= nm.getPostcode(); } } return ans }
Com isso:
public String getFirstName(Person person) { return person?.getName()?.getGivenName(); }
Eu vasculhei a internet (ok, eu gastei pelo menos 15 minutos pesquisando variações sobre "ponto de interrogação java") e não encontrei nada. Então, minha pergunta: existe alguma documentação oficial sobre isso? Descobri que C # tem um operador semelhante (o operador "??"), mas gostaria de obter a documentação da linguagem em que estou trabalhando. Ou isso é apenas um uso do operador ternário que eu nunca visto antes.
Obrigado!
EDITAR: Link para o artigo: http://infoworld.com/d/developer-world/12-programming-mistakes-avoid-292
A ?? B == (A != null) ? A : B
. Isso parece avaliar uma propriedade em um objeto se a referência do objeto não for nula, ou sejaA?.B == (A != null) ? A.B : null
.Respostas:
A ideia original vem do groovy. Foi proposto para Java 7 como parte do Projeto Coin: https://wiki.openjdk.java.net/display/Coin/2009+Proposals+TOC (Elvis e outros operadores nulos-seguros), mas ainda não foi aceito .
O operador Elvis relacionado?: Foi proposto como uma
x ?: y
abreviação dex != null ? x : y
, especialmente útil quando x é uma expressão complexa.fonte
x!=null ? x : y
?
é o topete de assinatura de Elvis Presley; o:
justo representa um par de olhos como de costume. Talvez?:-o
seja mais evocativo ...?:0
Deve ser um operador "Não é nulo, nem 0". Faça assim.Essa sintaxe não existe em Java, nem está programada para ser incluída em nenhuma das versões futuras que eu conheço.
fonte
Optional
emap
outras coisas. não estamos escondendo o problema se o valor for anulável, o que significa que às vezes é esperado que seja nulo e você terá que lidar com isso. às vezes, os valores padrão são perfeitamente razoáveis e não é uma prática ruim.Havia uma proposta para isso em Java 7, mas foi rejeitada:
http://tech.puredanger.com/java7/#null
fonte
Uma maneira de contornar a falta de "?" operador usando Java 8 sem a sobrecarga de try-catch (que também poderia ocultar um
NullPointerException
originado em outro lugar, como mencionado) é criar uma classe para "canalizar" métodos em um estilo 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; } }
Então, o exemplo dado seria:
public String getFirstName(Person person) { return Pipe.of(person).after(Person::getName).after(Name::getGivenName).get(); }
[EDITAR]
Pensando melhor, descobri que é realmente possível conseguir o mesmo apenas usando classes Java 8 padrão:
public String getFirstName(Person person) { return Optional.ofNullable(person).map(Person::getName).map(Name::getGivenName).orElse(null); }
Neste caso, é ainda possível escolher um valor padrão (like
"<no first name>"
) em vez denull
passá-lo como parâmetro deorElse
.fonte
Pipe
classe para adaptar umaorElse
funcionalidade para que eu pudesse passar um objeto não nulo arbitrário dentro doorElse
método?orElse
método àPipe
classe.Consulte: https://blogs.oracle.com/darcy/project-coin:-the-final-five-or-so (especificamente "Elvis e outros operadores seguros nulos").
O resultado é que esse recurso foi considerado para o Java 7, mas não foi incluído.
fonte
Na verdade, esse é o operador de desreferência segura do Groovy . Você não pode usá-lo em Java puro (infelizmente), então essa postagem está simplesmente errada (ou mais provavelmente um pouco enganosa, se estiver alegando que o Groovy é a "versão mais recente do Java").
fonte
Java não tem a sintaxe exata, mas a partir do JDK-8, temos a API opcional com vários métodos à nossa disposição. Portanto, a versão C # com o uso do operador condicional nulo :
return person?.getName()?.getGivenName();
pode ser escrito da seguinte forma em Java com a API opcional :
return Optional.ofNullable(person) .map(e -> e.getName()) .map(e -> e.getGivenName()) .orElse(null);
se algum de
person
,getName
ougetGivenName
for nulo, será retornado nulo.fonte
É possível definir métodos util que resolvem isso de uma maneira quase bonita com Java 8 lambda.
Esta é uma variação da solução H-MANs, mas usa métodos sobrecarregados com vários argumentos para lidar com várias etapas em vez de captura
NullPointerException
.Mesmo que eu ache que essa solução seja legal, eu prefiro a segunda de Helder Pereira, já que ela não requer nenhum método utilitário.
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; } }
fonte
Não tenho certeza se isso funcionaria; se, digamos, a referência de pessoa fosse nula, pelo que o tempo de execução a substituiria? Uma nova pessoa? Isso exigiria que a pessoa tivesse alguma inicialização padrão que você esperaria neste caso. Você pode evitar exceções de referência nula, mas ainda obteria um comportamento imprevisível se não planejasse esses tipos de configurações.
O ?? operador em C # pode ser denominado melhor o operador "coalescer"; você pode encadear várias expressões e ele retornará a primeira que não for nula. Infelizmente, o Java não tem. Acho que o melhor que você pode fazer é usar o operador ternário para realizar verificações de nulos e avaliar uma alternativa para a expressão inteira se algum membro da cadeia for nulo:
return person == null ? "" : person.getName() == null ? "" : person.getName().getGivenName();
Você também pode usar try-catch:
try { return person.getName().getGivenName(); } catch(NullReferenceException) { return ""; }
fonte
getName()
ougetGivenName()
você não saberá se simplesmente retornar uma string vazia para todas as ocorrências.person?.getName()?.getGivenName() ?? ""
é o equivalente ao seu primeiro exemplo, com a exceção de que segetGivenName()
retornar nulo, ainda dará""
Aí está, invocação null-safe em 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; } }
fonte
Se alguém estiver procurando uma alternativa para versões antigas do java, você pode tentar esta que escrevi:
/** * 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; } }
fonte
Você pode testar o código que forneceu e ele apresentará um erro de sintaxe. Portanto, não é compatível com Java. Groovy oferece suporte e foi proposto para Java 7 (mas nunca foi incluído).
No entanto, você pode usar o opcional fornecido em Java 8. Isso pode ajudá-lo a alcançar algo em linha semelhante. https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html
Exemplo de código para opcional
fonte
Como o Android não oferece suporte a Funções Lambda, a menos que seu sistema operacional instalado seja> = 24, precisamos usar reflexão.
// 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; } } }
fonte
Se este não for um problema de desempenho para você, você pode escrever
public String getFirstName(Person person) { try { return person.getName().getGivenName(); } catch (NullPointerException ignored) { return null; } }
fonte
getName()
lançado uma exceção internamente que você não quer jogar fora.Person
pode ser um proxy acessando um banco de dados ou alguma memória não heap e pode haver um bug em algum lugar ... Não é muito realista, mas, Peter, aposto que você nunca escreveu um trecho de código como o acima .