resumen
Cuando estaba libre, me tomaba el tiempo para aprender Groovy y Scala que se ejecuta en JVM, y descubrí que manejaban nulas mucho más con cautela que las versiones anteriores de Java. En Java 8, Opcional ofrece una solución muy elegante para el procesamiento nulo de programación funcional. Este artículo explicará el mal manejo de NULL en Java, y luego introducirá el uso de opcional para implementar la programación funcional de Java.
El nulo que nos molestó en esos años
Hay una leyenda en el mundo de Java: solo cuando realmente entiendes la excepción del puntero nulo, puedes ser considerado un desarrollador de Java calificado. En nuestra carrera de personaje de Java, nos encontraremos con varios procesos nulos todos los días. Podemos escribir códigos como los siguientes repetidamente todos los días:
if (null! = obj1) {if (null! = obje2) {// haz algo}}Si tiene un poco de visión, Javaer hará algunas cosas bonitas y encontrará una manera de juzgar a Null:
Boolean checkNotnull (object obj) {return null == obj? falso: verdadero; } void do () {if (checkNotNull (obj1)) {if (checkNotNull (obj2)) {// haz algo}}}Entonces, la pregunta vuelve: si un nulo representa una cadena vacía, ¿qué significa ""?
Entonces el pensamiento inercial nos dice: "" ¿Y NULL son los códigos de cadena vacíos? Simplemente actualicé el valor nulo:
boolean checkNotblank (object obj) {return null! = obj &&! "". iguales (obj)? verdadero: falso; } void do () {if (checkNotBlank (obj1)) {if (checkNotNull (obj2)) {// haz algo}}}Si tiene tiempo, puede consultar cuánto código ha escrito en el proyecto actual o en su código pasado y qué es similar al código anterior.
Me pregunto si has pensado seriamente en una pregunta: ¿Qué significa un nulo?
Recordando, ¿cuántas veces nos encontramos con java.lang.nullpointerexception excepciones en nuestra carrera de Gramming anterior? NullPointerException es una excepción de nivel RuntimeException que no es necesario capturar. Si lo manejamos accidentalmente, a menudo vemos varias salidas de la pila de excepciones causadas por NullPointerException en el registro de producción. Y en base a esta información de la pila de excepciones, no podemos localizar la causa del problema, porque no es el lugar donde se lanzó la NullPointerException, lo que causó el problema. Tenemos que profundizar para descubrir dónde se generó este nulo, y los registros a menudo no pueden rastrear en este momento.
A veces es aún más trágico que los lugares donde se producen los valores nulos a menudo no estén en nuestro propio código de proyecto. Esto tiene un hecho más vergonzoso: cuando llamamos varias interfaces de terceros de diferentes calidad, es difícil saber si una interfaz devuelve un nulo por casualidad ...
Vuelve al problema cognitivo anterior de NULL. Muchos Javaers creen que NULL significa "nada" o "valor no existe". De acuerdo con este pensamiento inercial, nuestra lógica de código es: llama a mi interfaz y devuelve el "valor" correspondiente de acuerdo con los parámetros que me da. Si esta condición no puede encontrar el "valor" correspondiente, entonces, por supuesto, le devolveré un nulo que no hay "cosa". Echemos un vistazo al siguiente código, que está escrito en un estilo de codificación Java muy tradicional y estándar:
clase myentity {int id; Nombre de cadena; Cadena getName () {nombre de retorno; }} // Prueba de clase MainPublic {public static void main (string [] args) final myentity myEntity = getMyEntity (falso); System.out.println (myEntity.getName ()); } private getMyEntity (boolean issuc) {if (issuc) {return new myEntity (); } else {return null; }}}Este código de código es muy simple, y el código de negocio diario es definitivamente mucho más complicado que este, pero de hecho, una gran cantidad de nuestros códigos Java se escriben de acuerdo con esta rutina. Las personas que saben cómo usar productos pueden ver de un vistazo que definitivamente lanzarán una NullPointerException al final. Sin embargo, cuando escribimos el código de negocio, rara vez pensamos en tratar con este posible nulo (tal vez el documento API se ha escrito muy claramente y devolverá NULL en algunos casos, pero ¿se asegura de que lea cuidadosamente el documento API antes de comenzar a escribir el código), hasta que llegamos a cierta etapa de las pruebas, un valor NullPointerException repentino de repente.
// Prueba de clase MainPublic {public static void main (string [] args) final myEntity myEntity = getMyEntity (falso); if (null! = myentity) {system.out.println (myentity.getName ()); } else {System.out.println ("Error"); }}}Piense cuidadosamente en los últimos años, ¿hemos hecho esto? Si no se pueden descubrir algunos problemas nulos hasta la fase de prueba, entonces el problema ahora es: ¿cuántos nulos no se procesan adecuadamente en esos códigos comerciales complejos y bien estructurados?
La madurez y el rigor de un proyecto a menudo se pueden ver cuando se trata de NULL. Por ejemplo, Guava dio un elegante método de procesamiento nulo antes de JDK1.6, que muestra cuán profundas son las habilidades.
Los nulos fantasmales nos impiden progresar
Si usted es un Javaer centrado en el desarrollo tradicional orientado a objetos, puede estar acostumbrado a los diversos problemas causados por NULL. Pero hace muchos años, el Gran Dios dijo que Null es un pozo.
Tony Hall (¿no sabes quién es este tipo? Ve a controlarlo tú mismo) una vez dijo: "Lo llamo mi error de mil millones de dólares. Fue la invención de la referencia nula en 1965. No pude resistir la tentación de hacer una referencia nula, simplemente porque fue muy fácil de implementar". (El significado general es: "Lo llamo la invención de NULL un error invaluable. Porque en el desierto de las computadoras en 1965, las referencias vacías eran demasiado fáciles de implementar, por lo que no pude resistir la tentación de inventar el puntero nulo").
Luego, veamos qué otros problemas introducirán NULL.
Consulte el siguiente código:
Dirección de cadena = persona.getCountry (). GetProvince (). GetCity ();
Si ha jugado algunos idiomas funcionales (Haskell, Erlang, Clojure, Scala, etc.), lo anterior es una forma de escribir muy natural. Por supuesto, el método de escritura anterior se puede implementar utilizando Java.
Pero para manejar perfectamente todas las excepciones nulas posibles, tenemos que cambiar este paradigma de programación de funciones elegantes a esto:
if (persona! = null) {país país = persona.getCountry (); if (country! = null) {provincia provincia = country.getProvince (); if (provincia! = null) {direcciones = provincia.getCity (); }}}En un instante, la programación funcional de alta calidad Java8 regresó hace 10 años. Tales juicios anidados aún son triviales, aumentando la cantidad de código y no son elegantes. Es más probable que suceda: durante la mayor parte del tiempo, las personas olviden juzgar el nulo que puede ocurrir, incluso las personas mayores que han escrito código durante muchos años no son la excepción.
La sección anterior del procesamiento nulo, que se anidada capa por capa, también es parte de Java tradicional criticada durante mucho tiempo. Si usa las versiones de Java tempranas como su lenguaje de la Ilustración, este olor de get-> si es nulo-> retorno lo afectará durante mucho tiempo (recuerde en una comunidad extranjera, que se llama: desarrollo orientado a la entidad).
Uso de opcional para implementar la programación funcional de Java
De acuerdo, después de hablar de todo tipo de problemas, podemos entrar en una nueva era.
Mucho antes del lanzamiento de la versión Java SE 8, otros lenguajes de desarrollo funcional similares tenían sus propias soluciones. Aquí está el código de Groovy:
Versión de cadena = computadora? .GetSoundCard () ?. getUsb ()?
Haskell utiliza un identificador de clase de tala para procesar valores nulos. Scala, conocida como un lenguaje de desarrollo de varios paradigmas, proporciona una opción [t] que es similar a quizás, para envolver y procesar nulo.
Java8 presenta java.util.optional <t> para manejar el problema nulo de la programación funcional. Las ideas de procesamiento de <t> opcionales son similares a las de Haskell y Scala, pero hay algunas diferencias. Echemos un vistazo al siguiente ejemplo de código Java:
Prueba de clase pública {public static void main (string [] args) {final de cadena text = "Hallo World!"; Opcional.ofnullable (texto) // Mostrar para crear un shell.map opcional (test :: print) .map (test :: imprim) .ifpresente (system.out :: println); Opcional.ofnullable (text) .map (s -> {system.out.println (s); return s.substring (6);}) .map (s -> null) // return null .ifpresent (system.out :: println); } // imprima e intercepta la cadena después de str [5] string static static print (string str) {system.out.println (str); devolver str.substring (6); }} // Salida de consol // num1: Hallo World! // num2: World! // num3: // num4: Hallo World!(Puede copiar el código anterior en su IDE, siempre que JDK8 debe estar instalado).
Se crean dos opciones en el código anterior, y las funciones implementadas son básicamente las mismas. Ambos usan opcional como el shell de cadena para truncar la cadena. Cuando se encuentra un valor nulo durante el procesamiento, el procesamiento ya no continuará. Podemos encontrar que después de que S-> NULL aparece en el segundo opcional, el posterior IfPresent ya no se ejecutará.
Preste atención a la salida // num3:, lo que significa que un "" carácter es salida, no un nulo.
Opcional proporciona interfaces ricas para manejar diversas situaciones, como modificar el código a:
Prueba de clase pública {public static void main (string [] args) {final de cadena text = "Hallo World!"; System.out.println (minúscula (texto)); // método un minúscula (null, system.out :: println); // método dos} cadena estática privada minúscula (string str) {return Opcional.ofnullable (str) .map (s -> s.tolowercase ()). Map (s-> s.replace ("world", "java"). Orele (") ("); } minúscula void estática privada (String Str, Consumer <String> Consumer) {Consumer.accept (minúscula (str)); }} // salida // hallo java! // nanDe esta manera, podemos procesar dinámicamente una cadena. Si se encuentra que el valor es nulo en cualquier momento, usamos ORELSE para devolver el predeterminado predeterminado "nan" predeterminado.
En general, podemos envolver cualquier estructura de datos en opcional y luego procesarla de manera funcional sin tener que preocuparse por los nulos que pueden aparecer en cualquier momento.
Echemos un vistazo a cómo Person.getCountry (). GetProvince (). GetCity () mencionado anteriormente no requiere un montón de IFS para manejarlo.
El primer método es no cambiar la entidad anterior:
import java.util.optional; prueba de clase pública {public static void main (string [] args) {system.out.println (opcional.ofnullable (nueva persona ()) .map (x-> x.country) .map (x-> x.provinec) .map (x-> x.city) .map (x-> x.name) .orelse ("unkonwn")); }} Persona de clase {Country Country;} Class Country {Province ProvisionEC;} Class Province {City City;} Class City {String Name;}Aquí, el opcional se usa como el caparazón devuelto cada vez. Si una determinada posición devuelve nulo, obtendrá "Unkonwn".
El segundo método es definir todos los valores en opcional:
import java.util.optional; public class test {public static void main (string [] args) {System.out.println (nueva persona () .country.flatmap (x -> x.provinec) .flatmap (provincia :: getcity) .flatmap (x -> x.name) .orelse ("sinkonwn");)););););););); }} Persona de clase {Opcional <Country> Country = Oppectional.Empty ();} Class Country {Opcional <CROVINCE> ProvisionC;} Class Province {Opcional <City> City; Opcional <city> getCity () {// para :: return City; }} class City {Opcional <String> name;}El primer método puede integrarse suavemente con Javabeans, entidad o POJA existentes sin ningún cambio, y puede integrarse más fácilmente en interfaces de terceros (como los frijoles de resorte). Se recomienda que el primer método opcional se use como método principal. Después de todo, no todos en el equipo pueden entender el propósito de cada get/set con un opcional.
Opcional también proporciona un método de filtro para filtrar datos (de hecho, interfaces de estilo flujo en Java 8 proporcionan métodos de filtro). Por ejemplo, en el pasado juzgamos que el valor existió y realizó el procesamiento correspondiente:
if (provincia! = null) {ciudad ciudad = provincia.getCity (); if (null! = City && "Guangzhou" .equals (City.getName ()) {System.out.println (City.getName ());} else {System.out.println ("Unkonwn");}}Ahora podemos modificarlo a
Opcional.ofnullable (provincia) .map (x-> x.city) .filter (x-> "guangzhou" .equals (x.getName ())) .map (x-> x.name) .orelse ("unkonw");En este punto, la introducción de programación funcional se completa utilizando Opcional. Además de los métodos mencionados anteriormente, Opcional también proporciona métodos basados en más necesidades, Orelseget, Orelsethrow, etc. Orelseget lanzará una excepción de puntero nulo debido al valor nulo, y Orelsethrow lanzará una excepción definida por el usuario cuando ocurra nulo. Puede ver la documentación de la API para obtener más información sobre todos los métodos.
Escrito al final
Opcional es solo la punta del iceberg de la programación funcional de Java. Es necesario combinar características como Lambda, Stream y FunctionInterface para comprender realmente la efectividad de la programación funcional Java8. Originalmente quería introducir algún código fuente opcional y principios operativos, pero Opcional mismo tiene muy poco código y no mucha interfaz API. Si lo piensas cuidadosamente, no hay nada que decir y lo omitirás.
Aunque opcional es elegante, personalmente siento que hay algunos problemas de eficiencia, pero aún no se ha verificado. Si alguien tiene algún dato, hágamelo saber.
No soy un "partidario de programación funcional". Desde la perspectiva de los gerentes de equipo, ya que cada pequeña dificultad de aprendizaje aumenta, el costo del uso del personal y la interacción del equipo serán más altos. Al igual que en la leyenda, Lisp puede tener treinta veces menos código que C ++ y es más eficiente en el desarrollo, pero si una empresa de TI convencional nacional realmente usa LISP para hacer proyectos, ¿dónde ir y cuánto cuesta obtener a estos tipos que usan LISP?
Pero aliento a todos a aprender y comprender las ideas de la programación funcional. Especialmente desarrolladores que solían ser invadidos por Java y aún no saben qué cambios traerá Java8, Java8 es una buena oportunidad. También se alienta a introducir nuevas funciones de Java8 en el proyecto actual. Un equipo que coopera durante mucho tiempo y un antiguo lenguaje de programación debe inyectar constantemente una nueva vitalidad, de lo contrario, se retirará si no avanza.
Lo anterior es la recopilación de información opcional de Java. Continuaremos agregando información relevante en el futuro. ¡Gracias por su apoyo para este sitio web!