Estaba leyendo un artículo vinculado a una historia de slashdot y encontré este pequeño dato:
Tome la última versión de Java, que intenta facilitar la verificación de puntero nulo al ofrecer una sintaxis abreviada para las pruebas de puntero sin fin. Solo agregar un signo de interrogación a cada invocación de método incluye automáticamente una prueba para punteros nulos, reemplazando un nido de ratas de declaraciones 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 }
Con este:
public String getFirstName(Person person) { return person?.getName()?.getGivenName(); }
Busqué en Internet (está bien, pasé al menos 15 minutos buscando en Google variaciones sobre el "signo de interrogación de Java") y no obtuve nada. Entonces, mi pregunta: ¿hay alguna documentación oficial sobre esto? Descubrí que C # tiene un operador similar (el operador "??"), pero me gustaría obtener la documentación para el idioma en el que estoy trabajando. O, ¿es solo un uso del operador ternario que he nunca antes visto.
¡Gracias!
EDITAR: Enlace al artículo: http://infoworld.com/d/developer-world/12-programming-mistakes-avoid-292
A ?? B == (A != null) ? A : B
. Esto parece evaluar una propiedad en un objeto si la referencia del objeto no es nula, es decirA?.B == (A != null) ? A.B : null
.Respuestas:
La idea original proviene de groovy. Se propuso para Java 7 como parte de Project Coin: https://wiki.openjdk.java.net/display/Coin/2009+Proposals+TOC (Elvis y otros operadores de seguridad nula), pero aún no se ha aceptado .
El operador de Elvis relacionado?: Se propuso para
x ?: y
abreviarx != null ? x : y
, especialmente útil cuando x es una expresión compleja.fuente
x!=null ? x : y
?
es el tupé característico de Elvis Presley; el:
justo representa un par de ojos como de costumbre. Quizás?:-o
sea más evocador ...?:0
Debe ser un operador "No nulo ni 0". Hazlo así.Esta sintaxis no existe en Java, ni está programada para ser incluida en ninguna de las próximas versiones que conozco.
fuente
Optional
ymap
esas cosas. no estamos ocultando el problema si el valor es anulable, lo que significa que se espera que sea nulo a veces y usted tiene que manejar eso. a veces los valores predeterminados son perfectamente razonables y no es una mala práctica.Hubo una propuesta para ello en Java 7, pero fue rechazada:
http://tech.puredanger.com/java7/#null
fuente
Una forma de solucionar la falta de "?" El operador que usa Java 8 sin la sobrecarga de try-catch (que también podría ocultar un
NullPointerException
originado en otro lugar, como se mencionó) es crear una clase para "canalizar" métodos en un 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; } }
Entonces, el ejemplo dado se convertiría en:
public String getFirstName(Person person) { return Pipe.of(person).after(Person::getName).after(Name::getGivenName).get(); }
[EDITAR]
Pensándolo bien, descubrí que en realidad es posible lograr lo mismo solo usando clases estándar de Java 8:
public String getFirstName(Person person) { return Optional.ofNullable(person).map(Person::getName).map(Name::getGivenName).orElse(null); }
En este caso, incluso es posible elegir un valor predeterminado (como
"<no first name>"
) en lugar denull
pasarlo como parámetro deorElse
.fuente
Pipe
clase para adaptar unaorElse
funcionalidad de modo que pudiera pasar un objeto arbitrario no nulo dentro delorElse
método?orElse
método a laPipe
clase.Ver: https://blogs.oracle.com/darcy/project-coin:-the-final-five-or-so (específicamente "Elvis y otros operadores de seguridad nula").
El resultado es que esta función se consideró para Java 7, pero no se incluyó.
fuente
Ese es en realidad el operador de desreferencia de cajas fuertes de Groovy . No puede usarlo en Java puro (lamentablemente), por lo que la publicación es simplemente incorrecta (o más probablemente un poco engañosa, si afirma que Groovy es la "última versión de Java").
fuente
Java no tiene la sintaxis exacta, pero a partir de JDK-8, tenemos la API opcional con varios métodos a nuestra disposición. Entonces, la versión C # con el uso del operador condicional nulo :
return person?.getName()?.getGivenName();
se puede escribir de la siguiente manera en Java con la API opcional :
return Optional.ofNullable(person) .map(e -> e.getName()) .map(e -> e.getGivenName()) .orElse(null);
si alguno de
person
,getName
ogetGivenName
es nulo, se devuelve nulo.fuente
Es posible definir métodos útiles que resuelvan esto de una manera casi bonita con Java 8 lambda.
Esta es una variación de la solución H-MAN, pero utiliza métodos sobrecargados con múltiples argumentos para manejar múltiples pasos en lugar de capturar
NullPointerException
.Incluso si creo que esta solución es algo genial, creo que prefiero el segundo de Helder Pereira, ya que no requiere ningún método útil.
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; } }
fuente
No estoy seguro de que esto funcione; si, digamos, la referencia de la persona fuera nula, ¿con qué la reemplazaría el tiempo de ejecución? ¿Una nueva persona? Eso requeriría que la Persona tenga alguna inicialización predeterminada que esperaría en este caso. Puede evitar las excepciones de referencia nula, pero aún así obtendrá un comportamiento impredecible si no planifica este tipo de configuraciones.
Los ?? El operador en C # podría denominarse mejor operador de "fusión"; puede encadenar varias expresiones y devolverá la primera que no es nula. Desafortunadamente, Java no lo tiene. Creo que lo mejor que puede hacer es usar el operador ternario para realizar comprobaciones nulas y evaluar una alternativa a la expresión completa si algún miembro de la cadena es nulo:
return person == null ? "" : person.getName() == null ? "" : person.getName().getGivenName();
También puede usar try-catch:
try { return person.getName().getGivenName(); } catch(NullReferenceException) { return ""; }
fuente
getName()
ogetGivenName()
que no sabrá si simplemente devuelve una cadena vacía para todas las ocurrencias.person?.getName()?.getGivenName() ?? ""
es el equivalente de su primer ejemplo, con la excepción de que sigetGivenName()
devuelve un valor nulo, todavía dará""
Ahí lo tienes, invocación de seguridad nula en 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; } }
fuente
Si alguien está buscando una alternativa para las versiones antiguas de Java, puede probar esta que escribí:
/** * 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; } }
fuente
Puede probar el código que ha proporcionado y dará un error de sintaxis, por lo que no es compatible con Java. Groovy lo admite y fue propuesto para Java 7 (pero nunca se incluyó).
Sin embargo, puede utilizar el Opcional proporcionado en Java 8. Esto podría ayudarlo a lograr algo en una línea similar. https://docs.oracle.com/javase/8/docs/api/java/util/Optional.html http://www.oracle.com/technetwork/articles/java/java8-optional-2175753.html
Código de ejemplo para opcional
fuente
Dado que Android no es compatible con Lambda Functions a menos que su sistema operativo instalado sea> = 24, debemos usar la reflexión.
// 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; } } }
fuente
Si esto no es un problema de rendimiento para usted, puede escribir
public String getFirstName(Person person) { try { return person.getName().getGivenName(); } catch (NullPointerException ignored) { return null; } }
fuente
getName()
lanzara una excepción internamente que no desea desechar.Person
podría haber un proxy que acceda a una base de datos o alguna memoria que no sea del montón y podría haber un error en alguna parte ... No es muy realista, pero, Peter, apuesto a que nunca has escrito un fragmento de código como el anterior. .