He leído en muchos sitios web. Opcional debe usarse solo como tipo de retorno y no en argumentos de método. Estoy luchando por encontrar una razón lógica por la cual. Por ejemplo, tengo una lógica que tiene 2 parámetros opcionales. Por lo tanto, creo que tendría sentido escribir la firma de mi método de esta manera (solución 1):
public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2 {
// my logic
}
Muchas páginas web especifican Opcional no deben usarse como argumentos de método. Con esto en mente, podría usar la siguiente firma del método y agregar un comentario claro de Javadoc para especificar que los argumentos pueden ser nulos, con la esperanza de que los futuros mantenedores lean el Javadoc y, por lo tanto, siempre realicen comprobaciones nulas antes de usar los argumentos (solución 2) :
public int calculateSomething(String p1, BigDecimal p2) {
// my logic
}
Alternativamente, podría reemplazar mi método con cuatro métodos públicos para proporcionar una interfaz más agradable y hacer que sea más obvio que p1 y p2 son opcionales (solución 3):
public int calculateSomething() {
calculateSomething(null, null);
}
public int calculateSomething(String p1) {
calculateSomething(p1, null);
}
public int calculateSomething(BigDecimal p2) {
calculateSomething(null, p2);
}
public int calculateSomething(String p1, BigDecimal p2) {
// my logic
}
Ahora trato de escribir el código de la clase que invoca esta pieza lógica para cada enfoque. Primero recupero los dos parámetros de entrada de otro objeto que devuelve Optional
sy luego invoco calculateSomething
. Por lo tanto, si se usa la solución 1, el código de llamada se vería así:
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1, p2);
si se usa la solución 2, el código de llamada se vería así:
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));
si se aplica la solución 3, podría usar el código anterior o podría usar lo siguiente (pero es significativamente más código):
Optional<String> p1 = otherObject.getP1();
Optional<BigInteger> p2 = otherObject.getP2();
int result;
if (p1.isPresent()) {
if (p2.isPresent()) {
result = myObject.calculateSomething(p1, p2);
} else {
result = myObject.calculateSomething(p1);
}
} else {
if (p2.isPresent()) {
result = myObject.calculateSomething(p2);
} else {
result = myObject.calculateSomething();
}
}
Entonces mi pregunta es: ¿Por qué se considera una mala práctica usar Optional
s como argumentos de método (ver solución 1)? Parece la solución más legible para mí y hace que sea más obvio que los parámetros podrían estar vacíos / nulos para futuros mantenedores. (Soy consciente de que los diseñadores Optional
pretendían que solo se usara como un tipo de retorno, pero no puedo encontrar ninguna razón lógica para no usarlo en este escenario).
null
?Respuestas:
Oh, esos estilos de codificación deben tomarse con un poco de sal.
En general: Opcional unifica dos estados, que tienen que ser desentrañados. Por lo tanto, es más adecuado para el resultado que la entrada, para la complejidad del flujo de datos.
fuente
Optional
parámetro representa uno de tres estados: unnull
Opcional, un no nuloOptional
conisPresent() == false
y un no nuloOptional
envolviendo un valor real.@NotNull Optional
.Optional
, el estado 1 (null
Opcional) generalmente indica un error de programación, por lo que es mejor que no lo maneje (solo deje que arroje unNullPointerException
)La mejor publicación que he visto sobre el tema fue escrita por Daniel Olszewski :
fuente
null
oOptional
.Casi no hay buenas razones para no usar
Optional
como parámetros. Los argumentos en contra de esto se basan en argumentos de la autoridad (ver Brian Goetz; su argumento es que no podemos aplicar opciones no nulas) o que losOptional
argumentos pueden ser nulos (esencialmente el mismo argumento). Por supuesto, cualquier referencia en Java puede ser nula, debemos alentar las reglas que impone el compilador, no la memoria de los programadores (lo cual es problemático y no se escala).Los lenguajes de programación funcional fomentan los
Optional
parámetros. Una de las mejores formas de usar esto es tener múltiples parámetros opcionales yliftM2
usar una función asumiendo que los parámetros no están vacíos y devolviendo un opcional (ver http://www.functionaljava.org/javadoc/4.4/functionaljava/fj/ data / Option.html # liftM2-fj.F- ). Desafortunadamente, Java 8 ha implementado una biblioteca muy limitada que admite opcional.Como programadores de Java, solo deberíamos usar null para interactuar con bibliotecas heredadas.
fuente
@Nonnull
y@Nullable
desde javax.annotation en su lugar? Los uso ampliamente en una biblioteca que desarrollo, y el soporte proporcionado por el IDE (IntelliJ) es muy bueno.@NonNull
es totalmente correcto si tienen el mismo propósito de diferentes maneras? Para mí, solo hay un argumento que tiene al menos algo de sentido: "Opcional debería usarse para proporcionar mejores API que tengan un tipo de retorno claro. Sin embargo, para argumentos de método, pueden usarse funciones sobrecargadas". - Aún así, no creo que este sea un argumento lo suficientemente fuerte.Este consejo es una variante de la regla general "sea lo más inespecífico posible con respecto a las entradas y lo más específico posible con respecto a las salidas".
Por lo general, si tiene un método que toma un valor simple no nulo, puede asignarlo sobre
Optional
, por lo que la versión simple es estrictamente más inespecífica con respecto a las entradas. Sin embargo, hay un montón de posibles razones por las cuales querrías requerir unaOptional
discusión:Optional
Optional
si el valor dado está vacíoPiensas queOptional
es tan asombroso que cualquiera que use tu API debería estar obligado a aprender sobre él;fuente
El patrón con
Optional
es para evitar volvernull
. Todavía es perfectamente posible pasarnull
a un método.Si bien estos aún no son realmente oficiales, puede usar anotaciones de estilo JSR-308 para indicar si acepta o no
null
valores en la función. Tenga en cuenta que tendría que tener las herramientas adecuadas para identificarlo realmente, y proporcionaría más de una comprobación estática que una política de tiempo de ejecución exigible, pero ayudaría.fuente
Optional
tampoco soluciona el problema, de ahí mi sugerencia para las anotaciones.Esto me parece un poco tonto, pero la única razón por la que puedo pensar es que los argumentos de objeto en los parámetros del método ya son opcionales de alguna manera, pueden ser nulos. Por lo tanto, obligar a alguien a tomar un objeto existente y envolverlo en una opción no tiene sentido.
Dicho esto, encadenar métodos que toman / devuelven opciones es algo razonable, por ejemplo, tal vez mónada.
fuente
Optional
tiene sentido por completo?Echa un vistazo a JavaDoc en JDK10, https://docs.oracle.com/javase/10/docs/api/java/util/Optional.html , se agrega una nota de API:
fuente
Mi opinión es que Opcional debería ser una Mónada y estos no son concebibles en Java.
En la programación funcional, se trata de funciones puras y de orden superior que toman y componen sus argumentos únicamente en función de su "tipo de dominio comercial". La composición de funciones que se alimentan, o cuyo cálculo debe informarse, al mundo real (los llamados efectos secundarios) requiere la aplicación de funciones que se encarguen de desempaquetar automáticamente los valores de las mónadas que representan el mundo exterior (Estado, Configuración, Futuros, Quizás, Cualquiera, Escritor, etc ...); Esto se llama levantar. Puedes pensarlo como una especie de separación de preocupaciones.
Mezclar estos dos niveles de abstracción no facilita la legibilidad, por lo que es mejor evitarlo.
fuente
Otra razón para tener cuidado al pasar un
Optional
parámetro as es que un método debe hacer una cosa ... Si pasa unOptional
parámetro, podría favorecer hacer más de una cosa, podría ser similar a pasar un parámetro booleano.fuente
if(param.isPresent())
no es el mejor enfoque para usar Opcional, en su lugar puede usar:param.forEach(() -> {...})
Aceptar Opcional como parámetros provoca un ajuste innecesario a nivel de la persona que llama.
Por ejemplo en el caso de:
Suponga que tiene dos cadenas no nulas (es decir, devueltas por algún otro método):
Estás obligado a envolverlos en Opcional incluso si sabes que no están vacíos.
Esto empeora aún más cuando tienes que componer con otras estructuras "asignables", es decir. Eithers :
árbitro:
árbitro. https://github.com/teamdigitale/digital-citizenship-functions/pull/148#discussion_r170862749
fuente
Creo que es porque usualmente escribes tus funciones para manipular datos, y luego las levantas para
Optional
usarmap
funciones similares. Esto le agrega elOptional
comportamiento predeterminado . Por supuesto, puede haber casos en los que sea necesario escribir su propia función auxiliar que funcioneOptional
.fuente
Creo que la resonancia del ser es que primero debe verificar si Opcional es nulo en sí mismo y luego tratar de evaluar el valor que envuelve. Demasiadas validaciones innecesarias.
fuente
Los opcionales no están diseñados para este propósito, como lo explicó Brian Goetz .
Siempre puede usar @Nullable para denotar que un argumento de método puede ser nulo. El uso de un opcional no le permite realmente escribir la lógica de su método de manera más clara.
fuente
Un enfoque más, lo que puedes hacer es
Una vez que haya creado una función (proveedor en este caso) podrá pasarla como cualquier otra variable y podrá llamarla utilizando
La idea aquí es si tiene un valor opcional o no será un detalle interno de su función y no estará en el parámetro. Pensar funciones cuando se piensa en opcional como parámetro es en realidad una técnica muy útil que he encontrado.
En cuanto a su pregunta si realmente debería hacerlo o no, se basa en su preferencia, pero como otros dijeron, hace que su API sea fea, por decir lo menos.
fuente
Al principio, también preferí pasar los Opcionales como parámetro, pero si cambia de una perspectiva de Diseñador de API a una perspectiva de Usuario de API, verá las desventajas.
Para su ejemplo, donde cada parámetro es opcional, sugeriría cambiar el método de cálculo en una clase propia como la siguiente:
fuente
Sé que esta pregunta se trata más de opiniones que de hechos concretos. Pero recientemente pasé de ser un desarrollador de .net a uno de Java, por lo que solo me uní recientemente a la fiesta Opcional. Además, preferiría decir esto como un comentario, pero dado que mi nivel de puntos no me permite comentar, me veo obligado a poner esto como una respuesta.
Lo que he estado haciendo, que me ha servido bien como regla general. Es usar los opcionales para los tipos de retorno, y solo usar los opcionales como parámetros, si requiero tanto el valor de Opcional como el clima o no, el Opcional tenía un valor dentro del método.
Si solo me importa el valor, verifico isPresent antes de llamar al método, si tengo algún tipo de registro o lógica diferente dentro del método que depende de si el valor existe, entonces felizmente pasaré el Opcional.
fuente
Esto se debe a que tenemos requisitos diferentes para un usuario de API y un desarrollador de API.
Un desarrollador es responsable de proporcionar una especificación precisa y una implementación correcta. Por lo tanto, si el desarrollador ya sabe que un argumento es opcional, la implementación debe tratarlo correctamente, ya sea un valor nulo u opcional. La API debe ser lo más simple posible para el usuario, y nulo es lo más simple.
Por otro lado, el resultado se pasa del desarrollador de API al usuario. Sin embargo, la especificación es completa y detallada, aún existe la posibilidad de que el usuario no lo sepa o simplemente sea flojo para tratarla. En este caso, el resultado Opcional obliga al usuario a escribir un código adicional para lidiar con un posible resultado vacío.
fuente
En primer lugar, si está utilizando el método 3, puede reemplazar esas últimas 14 líneas de código con esto:
int result = myObject.calculateSomething(p1.orElse(null), p2.orElse(null));
Las cuatro variaciones que escribió son métodos convenientes . Solo debes usarlos cuando sean más convenientes. Ese es también el mejor enfoque. De esa manera, la API tiene muy claro qué miembros son necesarios y cuáles no. Si no desea escribir cuatro métodos, puede aclarar las cosas por cómo nombra sus parámetros:
public int calculateSomething(String p1OrNull, BigDecimal p2OrNull)
De esta manera, está claro que se permiten valores nulos.
Su uso de
p1.orElse(null)
ilustra cuán detallado es nuestro código cuando se usa Opcional, lo cual es parte de por qué lo evito. Opcional fue escrito para la programación funcional. Las corrientes lo necesitan. Sus métodos probablemente nunca deberían devolver Opcional a menos que sea necesario usarlos en la programación funcional. Hay métodos, comoOptional.flatMap()
método, que requieren una referencia a una función que devuelve Opcional. Aquí está su firma:public <U> Optional<U> flatMap(Function<? super T, ? extends Optional<? extends U>> mapper)
Por lo general, esa es la única buena razón para escribir un método que devuelve Opcional. Pero incluso allí, se puede evitar. Puede pasar un captador que no devuelve Opcional a un método como flatMap (), envolviéndolo en otro método que convierta la función al tipo correcto. El método de envoltura se ve así:
Supongamos que tienes un captador como este:
String getName()
No puede pasarlo a flatMap así:
opt.flatMap(Widget::getName) // Won't work!
Pero puedes pasarlo así:
opt.flatMap(optFun(Widget::getName)) // Works great!
Fuera de la programación funcional, se deben evitar los opcionales.
Brian Goetz lo dijo mejor cuando dijo esto:
La razón por la que se agregó Opcional a Java es porque esto:
es más limpio que esto:
fuente