update tutorial descargar java java-8 optional

tutorial - java 8 update 40



¿Por qué no debería usarse Java 8''s Opcional en argumentos (19)

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 de 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 los Optional como argumentos de método (ver solución 1)? Me parece la solución más fácil de leer 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 de 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).


Aceptar Opcional como parámetros provoca un ajuste innecesario a nivel de la persona que llama.

Por ejemplo en el caso de:

public int calculateSomething(Optional<String> p1, Optional<BigDecimal> p2 {}

Suponga que tiene dos cadenas no nulas (es decir, devueltas por algún otro método):

String p1 = "p1"; String p2 = "p2";

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 :

Either<Error, String> value = compute().right().map((s) -> calculateSomething( < here you have to wrap the parameter in a Optional even if you know it''s a string >));

árbitro:

los métodos no deberían esperar la Opción como parámetros, esto casi siempre es un olor a código que indica una fuga de flujo de control de la persona que llama a la persona que llama, debe ser responsabilidad de la persona que llama verificar el contenido de una Opción

árbitro. https://github.com/teamdigitale/digital-citizenship-functions/pull/148#discussion_r170862749


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:

Optional<String> p1 = otherObject.getP1(); Optional<BigInteger> p2 = otherObject.getP2(); MyCalculator mc = new MyCalculator(); p1.map(mc::setP1); p2.map(mc::setP2); int result = mc.calculate();


Aunque ese tema es antiguo, estoy interesado en él ahora.

Personalmente, prefiero la solución 3, mientras que proporcionar un argumento a un método siempre se considera no nulo (me gusta agregar lomboks @NonNull). Entonces, si realmente desea aceptar sus 4 casos, ¿por qué no con un objeto de parámetro? Tal vez su otro objeto puede satisfacer? En general, preguntaría si realmente quiero proporcionar una API que acepte esos valores (opcionales) o ¿puedo volver a diseñar para evitar parámetros opcionales? Si crees que realmente necesitas parámetros opcionales, supongo que rompes SRP.


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 los argumentos Optional pueden ser nulos (esencialmente el mismo argumento). Por supuesto, cualquier referencia en Java puede ser nula, necesitamos alentar las reglas que impone el compilador, no la memoria de los programadores (lo cual es problemático y no escala).

Los lenguajes de programación funcional fomentan los parámetros Optional . Una de las mejores formas de usar esto es tener múltiples parámetros opcionales y usar liftM2 para 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.



Creo que es porque usualmente escribes tus funciones para manipular datos y luego las levantas a Optional usando el map y funciones similares. Esto le agrega el comportamiento Optional predeterminado. Por supuesto, puede haber casos en los que sea necesario escribir su propia función auxiliar que funcione en Optional .


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.


El patrón con Optional es para evitar volver null . Todavía es perfectamente posible pasar null 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 valores null en la función. Tenga en cuenta que tendría que tener las herramientas adecuadas para identificarlo realmente, y proporcionaría más una comprobación estática que una política de tiempo de ejecución exigible, pero ayudaría.

public int calculateSomething(@NotNull final String p1, @NotNull final String p2) {}


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 se vuelve 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, como el método Optional.flatMap() , 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 getter 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í:

public static <T, U> Function<? super T, Optional<U>> optFun(Function<T, U> function) { return t -> Optional.ofNullable(function.apply(t)); }

Supongamos que tiene un getter 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:

return Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods()) .stream() .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName()) .filter(m -> Arrays.equals(m.getParameterTypes(), parameterClasses)) .filter(m -> Objects.equals(m.getReturnType(), returnType)) .findFirst() .getOrThrow(() -> new InternalError(...));

es más limpio que esto:

Method matching = Arrays.asList(enclosingInfo.getEnclosingClass().getDeclaredMethods()) .stream() .filter(m -> Objects.equals(m.getName(), enclosingInfo.getName()) .filter(m -> Arrays.equals(m.getParameterTypes(), parameterClasses)) .filter(m -> Objects.equals(m.getReturnType(), returnType)) .getFirst(); if (matching == null) throw new InternalError("Enclosing method not found"); return matching;


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 a 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 que desearía requerir un argumento Optional :

  • desea que su función se use junto con otra API que devuelve un Optional
  • Su función debe devolver algo diferente a un vacío Optional si el valor dado está vacío
  • Piensas que Optional es tan increíble que cualquiera que use tu API debería estar obligado a conocerlo ;-)

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 de hacer, por ejemplo, tal vez mónada.


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, todavía 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.


La mejor publicación que he visto sobre el tema fue escrita por Daniel Olszewski :

Aunque puede ser tentador considerar Opcional para parámetros de método no obligatorios, tal solución palidece en comparación con otras posibles alternativas. Para ilustrar el problema, examine la siguiente declaración de constructor:

public SystemMessage(String title, String content, Optional<Attachment> attachment) { // assigning field values }

A primera vista, puede parecer una decisión de diseño correcta. Después de todo, marcamos explícitamente el parámetro adjunto como opcional. Sin embargo, en cuanto a llamar al constructor, el código del cliente puede volverse un poco torpe.

SystemMessage withoutAttachment = new SystemMessage("title", "content", Optional.empty()); Attachment attachment = new Attachment(); SystemMessage withAttachment = new SystemMessage("title", "content", Optional.ofNullable(attachment));

En lugar de proporcionar claridad, los métodos de fábrica de la clase Opcional solo distraen al lector. Tenga en cuenta que solo hay un parámetro opcional, pero imagine tener dos o tres. El tío Bob definitivamente no estaría orgulloso de ese código 😉

Cuando un método puede aceptar parámetros opcionales, es preferible adoptar el enfoque bien probado y diseñar tal caso utilizando la sobrecarga del método. En el ejemplo de la clase SystemMessage, declarar dos constructores separados es superior al uso de Opcional.

public SystemMessage(String title, String content) { this(title, content, null); } public SystemMessage(String title, String content, Attachment attachment) { // assigning field values }

Ese cambio hace que el código del cliente sea mucho más simple y fácil de leer.

SystemMessage withoutAttachment = new SystemMessage("title", "content"); Attachment attachment = new Attachment(); SystemMessage withAttachment = new SystemMessage("title", "content", attachment);


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.


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 (llamados efectos secundarios) requiere la aplicación de funciones que se encargan 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.


Oh, esos estilos de codificación deben tomarse con un poco de sal.

  1. (+) Pasar un resultado Opcional a otro método, sin ningún análisis semántico; dejando eso al método, está bastante bien.
  2. (-) El uso de parámetros opcionales que causan lógica condicional dentro de los métodos es literalmente contraproducente.
  3. (-) La necesidad de empaquetar un argumento en un Opcional, es subóptima para el compilador y realiza un ajuste innecesario.
  4. (-) En comparación con los parámetros anulables Opcional es más costoso.

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.


Otra razón para tener cuidado al pasar un parámetro Optional como es que un método debe hacer una cosa ... Si pasa un parámetro Optional , podría favorecer hacer más de una cosa, podría ser similar a pasar un parámetro booleano.

public void method(Optional<MyClass> param) { if(param.isPresent()) { //do something } else { //do some other } }


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.


Un enfoque más, lo que puedes hacer es

// get your optionals first Optional<String> p1 = otherObject.getP1(); Optional<BigInteger> p2 = otherObject.getP2(); // bind values to a function Supplier<Integer> calculatedValueSupplier = () -> { // your logic here using both optional as state}

Una vez que haya creado una función (proveedor en este caso) podrá pasarla como cualquier otra variable y podrá llamarla utilizando

calculatedValueSupplier.apply();

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.