resumo
Quando eu estava livre, eu levava um tempo para aprender groovy e scala correndo na JVM e descobri que eles lidam com nulos com muito mais cautela do que as versões anteriores do Java. No Java 8, o opcional fornece uma solução muito elegante para o processamento nulo de programação funcional. Este artigo explicará o mau manuseio de NULL em Java e, em seguida, apresentará o uso do Opcional para implementar a programação funcional do Java.
O nulo que nos incomodou naqueles anos
Há uma lenda no mundo Java: somente quando você realmente entende a exceção do ponteiro nulo, pode ser considerado um desenvolvedor de Java qualificado. Em nossa carreira de personagem Java, encontraremos vários processos nulos todos os dias. Podemos escrever códigos como os seguintes repetidamente todos os dias:
if (null! = obj1) {if (null! = obje2) {// faça algo}}Se você tem uma pequena visão, Javaer fará algumas coisas bonitas e encontrará uma maneira de julgar o NULL:
checknOtNull (object obj) {return null == obj? Falso: Verdadeiro; } void do () {if (checkNotNull (obj1)) {if (checkNotNull (obj2)) {// faça algo}}}Então, a pergunta vem novamente: se um nulo representa uma corda vazia, o que significa ""?
Então o pensamento inercial nos diz: "" e nulos são os códigos de string vazios? Eu simplesmente atualizei o valor nulo:
CheckNotBlank (objeto obj) {return null! = obj &&! "". Equals (obj)? Verdadeiro: falso; } void do () {if (checkNotBlank (obj1)) {if (checkNotNull (obj2)) {// faça algo}}}Se você tiver tempo, pode verificar quanto código você escreveu no projeto atual ou no seu código anterior e o que é semelhante ao código acima.
Gostaria de saber se você pensou seriamente em uma pergunta: o que significa um nulo?
Lembrando, quantas vezes encontramos Java.lang.NullPointerException Exceções em nossa carreira anterior? NullPointerException é uma exceção do nível de execução de tempo de execução que não precisa ser capturada. Se lidarmos acidentalmente, geralmente vemos várias saídas de pilha de exceção causadas pela NullPointerException no log de produção. E com base nessa informação de pilha de exceção, não podemos localizar a causa do problema, porque não é o local onde a NullPointerException foi lançada, o que causou o problema. Temos que ir mais fundo para descobrir onde esse nulo foi gerado, e os troncos geralmente não podem ser rastreados neste momento.
Às vezes, é ainda mais trágico que os locais onde os valores nulos são produzidos geralmente não estejam em nosso próprio código de projeto. Isso tem um fato mais embaraçoso - quando chamamos várias interfaces de terceiros de qualidade diferente, é difícil dizer se uma interface retorna um nulo por acaso ...
Volte ao problema cognitivo anterior do NULL. Muitos javaers acreditam que nulo significa "nada" ou "valor não existe". De acordo com esse pensamento inercial, nossa lógica de código é: você chama minha interface e retorna o "valor" correspondente de acordo com os parâmetros que você me dá. Se essa condição não puder encontrar o "valor" correspondente, é claro que retornarei um nulo para você que não há "coisa". Vamos dar uma olhada no código a seguir, que é escrito em um estilo de codificação Java muito tradicional e padrão:
classe myEntity {int id; Nome da string; String getName () {Nome de return; }} // Teste da classe MainPublic {public static void main (string [] args) myentity final myEntity = getMyEntity (false); System.out.println (myEntity.getName ()); } private getMyEntity (boolean ISSUC) {if (ISSUC) {return New MyEntity (); } else {return null; }}}Esse código é muito simples e o código comercial diário é definitivamente muito mais complicado do que isso, mas, na verdade, um grande número de nossos códigos Java são escritos de acordo com essa rotina. As pessoas que sabem usar mercadorias podem ver de relance que definitivamente lançarão uma NullPointerException no final. No entanto, quando escrevemos o código comercial, raramente pensamos em lidar com esse possível nulo (talvez o documento da API tenha sido escrito com muita clareza e retornará nulo em alguns casos, mas você garante que você leia cuidadosamente o documento da API antes de começar a escrever o código?) Até que chegamos a um determinado estágio, que nos devolvemos, que se deparamos com a devolução de um que se deparamos com a devolução de um que se deparamos repentinamente.
// Teste de classe MainPublic {public static void main (string [] args) final myentity myentity = getMyEntity (false); if (null! = myEntity) {System.out.println (myEntity.getName ()); } else {System.out.println ("erro"); }}}Pense cuidadosamente nos últimos anos, todos nós fizemos isso? Se alguns problemas nulos não puderem ser descobertos até a fase de teste, o problema é agora - quantos nulos não são processados adequadamente nesses códigos de negócios complexos e bem estruturados?
A maturidade e o rigor de um projeto geralmente podem ser vistos ao lidar com NULL. Por exemplo, a goiaba deu um elegante método de processamento nulo antes do JDK1.6, que mostra o quão profunda as habilidades são.
Nulos fantasmagóricos nos impedem de progredir
Se você é um Javaer com foco no desenvolvimento tradicional orientado a objetos, pode estar acostumado aos vários problemas causados por NULL. Mas muitos anos atrás, o grande Deus disse que Null é um poço.
Tony Hall (você não sabe quem é esse cara? Vá verificar você mesmo) uma vez: "Eu chamo isso de meu erro de bilhões de dólares. Foi a invenção da referência nula em 1965. Não pude resistir à tentação de fazer uma referência nula, simplesmente porque era muito fácil de implementar". (O significado geral é: "Eu chamo isso de invenção de um erro de valor inestimável. Porque no deserto dos computadores em 1965, referências vazias eram muito fáceis de implementar, então não pude resistir à tentação de inventar o ponteiro nulo").
Então, vamos ver quais outros problemas serão introduzir.
Confira o seguinte código:
String endereço = PERSON.GETCountry (). GetProvince (). GetCity ();
Se você tocou alguns idiomas funcionais (Haskell, Erlang, Clojure, Scala, etc.), o acima é uma maneira muito natural de escrever. Obviamente, o método de escrita acima pode ser implementado usando Java.
Mas, para lidar com todas as exceções nulas possíveis perfeitamente, temos que mudar esse paradigma de programação de funções elegantes para isso:
if (pessoa! = null) {country = Person.getCountry (); if (country! = null) {Província da província = country.getProvince (); if (província! = null) {endereço = província.getCity (); }}}Em um instante, a programação funcional de alta qualidade Java8 retornou a 10 anos atrás. Tais julgamentos aninhados ainda são triviais, aumentando a quantidade de código e sendo deselegantes. É mais provável que aconteça: na maioria das vezes, as pessoas esquecerão de julgar o nulo que pode ocorrer, mesmo pessoas idosas que escreveram código por muitos anos não são exceção.
A seção acima do processamento nulo, que é aninhado camada por camada, também faz parte do Java tradicional criticado por um longo tempo. Se você usar as versões Java iniciais como sua linguagem iluminista, esse odor de Get-> se NULL-> Retornar o afetará por um longo tempo (lembre-se de uma comunidade estrangeira, que é chamada: Desenvolvimento Orientado a Entidade).
Usando opcional para implementar a programação funcional do Java
Ok, depois de falar sobre todos os tipos de problemas, podemos entrar em uma nova era.
Muito antes do lançamento da versão Java SE 8, outras linguagens de desenvolvimento funcional similares tinham suas próprias soluções. Aqui está o código de Groovy:
String versão = computador? .GetSoundCard () ?. getusb () ?. getversion (): "Unkonwn";
Haskell usa um identificador de classe talvez de tipo para processar valores nulos. A Scala, conhecida como linguagem de desenvolvimento de vários paradigmas, oferece uma opção [T] que é semelhante a talvez, para embalagem e processamento nulo.
O Java8 apresenta java.util.optional <t> para lidar com o problema nulo de programação funcional. As idéias de processamento de <t> opcionais são semelhantes às de Haskell e Scala, mas existem algumas diferenças. Vamos dar uma olhada no seguinte exemplo do código Java:
public class Test {public static void main (string [] args) {final string text = "Hallo World!"; Opcional.ofnullable (text) // Mostrar para criar um shell.map opcional (test :: print) .map (test :: print) .Ifpresent (system.out :: println); Opcional.ofnullable (text) .map (s -> {System.out.println (s); retorna s.substring (6);}) .map (s -> null) // return null .Ifpresent (System.out.out: Println); } // Imprima e intercepte a string após str [5] String estática privada print (string str) {System.out.println (str); retornar str.substring (6); }} // consol saída // num1: hallo world! // num2: mundo! // num3: // num4: Hallo World!(Você pode copiar o código acima no seu IDE, desde que o JDK8 seja instalado.)
Dois opcionais são criados no código acima e as funções implementadas são basicamente as mesmas. Ambos usam opcional como shell de string para truncar a string. Quando um valor nulo é encontrado durante o processamento, o processamento não será mais continuado. Podemos descobrir que, depois que o S-> nulo aparece no segundo opcional, o ifpresent subsequente não será mais executado.
Preste atenção à saída // num3:, o que significa que um caractere "" é emitido, não um nulo.
Opcional fornece interfaces ricas para lidar com várias situações, como modificar o código para:
public class Test {public static void main (string [] args) {final string text = "Hallo World!"; System.out.println (minúsculo (texto)); // método um minúsculo (nulo, system.out :: println); // método dois} string estática privada minúscula (string str) {return opcional.ofnullable (str) .Map (s -> s.Tolowercase). } private estático vazio minúsculo (string str, consumidor <string> consumer) {consumer.accept (minúsculo (str)); }} // output // hallo java! // nanDessa forma, podemos processar dinamicamente uma string. Se o valor for considerado nulo a qualquer momento, usamos Orelse para retornar a predefinição "NAN".
Em geral, podemos embrulhar qualquer estrutura de dados opcional e depois processá -la de maneira funcional sem precisar se preocupar com nulos que podem aparecer a qualquer momento.
Vamos dar uma olhada em como a pessoa.getCountry (). GetProvince (). GetCity () mencionado anteriormente não requer um monte de IFs para lidar com isso.
O primeiro método é não mudar a entidade anterior:
importar java.util.optional; public class Test {public static void main (string [] args) {System.out.println (opcional.ofnullable (new Person ()) .map (x-> x.country) .map (x-> x.provinec) .Map (x-> x.ity). .orelse ("Unkonwn")); }} classe Pessoa {country;} classe country {Província provisionec;} Província de classe {City City;} classe City {Nome da String;}Aqui, opcional é usado quando o shell retornou sempre. Se uma certa posição retornar nulo, você receberá "Unkonwn".
O segundo método é definir todos os valores em opcional:
importar java.util.OptionCal; public class Test {public static void main (string [] args) {System.out.println (new Person () .country.flatmap (x -> x.provinec) .flatmap (Província :: getCity) .flatmap (X -> x.name). }} classe Pessoa {opcional <Country> country = opcional.Empty ();} Class country {opcional <víncia> provisionC;} Província de classe {opcional <city> city; Opcional <city> getCity () {// for :: Return City; }} classe City {opcional <string> nome;}O primeiro método pode ser suavemente integrado aos javabeus existentes, entidade ou poja sem nenhuma alteração e pode ser mais facilmente integrado em interfaces de terceiros (como feijão de mola). Recomenda -se que o primeiro método opcional seja usado como o método principal. Afinal, nem todos na equipe podem entender o objetivo de cada um Get/Set com um opcional.
Opcional também fornece um método de filtro para filtrar dados (de fato, interfaces em estilo de fluxo no Java 8 fornecem métodos de filtro). Por exemplo, no passado, julgamos que o valor existia e fazia processamento correspondente:
if (província! = null) {city city = provide.getCity (); if (null! = city && "guangzhou" .equals (city.getName ()) {System.out.println (city.getName ());} else {System.out.println ("unkonwn");}}Agora podemos modificar para
Opcional.ofnullable (Província) .map (x-> x.city) .Filter (x-> "guangzhou" .equals (x.getName ()) .Map (x-> x.name) .orelse ("unkonw");Neste ponto, a introdução da programação funcional é concluída usando opcional. Além dos métodos mencionados acima, o opcional também fornece métodos com base em mais necessidades, Orelseget, OrelSethrow, etc. Orelseget lançará uma exceção de ponteiro nulo devido ao valor nulo, e o OrrelSethrow lançará uma exceção definida pelo usuário quando ocorre o nulo. Você pode visualizar a documentação da API para saber mais sobre todos os métodos.
Escrito no final
Opcional é apenas a ponta do iceberg da programação funcional de Java. É necessário combinar recursos como Lambda, Stream e FunctionInterface para entender verdadeiramente a eficácia da programação funcional Java8. Originalmente, eu queria introduzir alguns código -fonte opcional e princípios operacionais, mas a própria opção tem muito pouco código e não muita interface da API. Se você pensar com cuidado, não há nada a dizer e você omitirá.
Embora opcional seja elegante, eu pessoalmente sinto que existem alguns problemas de eficiência, mas ainda não foi verificado. Se alguém tiver algum dado, entre em contato.
Eu não sou um "apoiador de programação funcional". Do ponto de vista dos gerentes de equipe, para cada pouca dificuldade de aprendizado aumentar, o custo do uso do pessoal e da interação da equipe será maior. Assim como em Legend, o LISP pode ter trinta vezes menos código que o C ++ e é mais eficiente em desenvolvimento, mas se uma empresa de TI convencional doméstica realmente usa o LISP para fazer projetos, para onde ir e quanto custa para conseguir esses caras que usam Lisp?
Mas eu encorajo todos a aprender e entender as idéias da programação funcional. Especialmente desenvolvedores que costumavam ser invadidos por Java e ainda não sabem quais mudanças Java8 trará, o Java8 é uma boa oportunidade. Também é incentivado a introduzir novos recursos Java8 no projeto atual. Uma equipe que coopera por um longo tempo e uma linguagem de programação antiga precisa injetar constantemente nova vitalidade; caso contrário, você recuará se não avançar.
O exposto acima é a coleção de informações opcionais Java. Continuaremos a adicionar informações relevantes no futuro. Obrigado pelo seu apoio a este site!