introdução
O NPE (NullPointerException) é a exceção mais comum nos programas de depuração. O Google tem muitas discussões sobre se o método deve retornar um objeto nulo ou novo um objeto vazio.
No início do artigo, vamos falar sobre o problema do NPE primeiro. O problema do NPE é a NullPointerException que geralmente encontramos no desenvolvimento. Suponha que tenhamos duas classes, e seu diagrama de classe UML é mostrado na figura a seguir
Nesse caso, há o seguinte código
user.getAddress (). getProvince ();
Essa maneira de escrever pode relatar uma NullPointerException quando o usuário é nulo. Para resolver esse problema, o seguinte método de escrita é adotado
if (usuário! = null) {endereço de endereço = user.getAddress (); if (endereço! = null) {província de string = endereço.getProvince (); }}Este estilo de escrita é relativamente feio. Para evitar o estilo de escrita acima, o design feio se torna elegante. O Java8 fornece aula opcional para otimizar este método de escrita, e a seção de texto a seguir será explicada em detalhes.
Introdução da API
Deixe -me primeiro apresentar a API. Ao contrário de outros artigos, este artigo adota um método de analogia e combina o código -fonte. Ao contrário de outros artigos, cada lista de API torna as pessoas incapazes de encontrar os pontos -chave.
(1) opcional (valor t), vazio (), de (valor t), ofnullable (valor t)
Essas quatro funções têm correlações, portanto são colocadas em um grupo para memória.
Deixe -me primeiro explicar que o opcional (valor t), ou seja, o construtor, é permissão privada e não pode ser chamada externamente. As outras três funções são permissões públicas para ligarmos. Então, a essência do opcional é armazenar um valor real internamente e, ao construir, é julgado diretamente se seu valor está vazio. OK, isso ainda é bastante abstrato. Carregue diretamente o código -fonte do construtor opcional (valor t), como mostrado na figura abaixo
Então, o código -fonte de (valor t) é o seguinte
public static <t> opcional <t> de (valor t) {retorna novo opcional <> (valor); } Em outras palavras, o construtor é chamado internamente pela função (valor t). Com base no código -fonte do construtor, podemos tirar duas conclusões:
(1) Quando o valor do valor estiver vazio, uma NullPointerException ainda será relatada.
(2) O objeto opcional construído pela função (valor t) pode ser construído normalmente quando o valor do valor não está vazio.
Além disso, a classe opcional também mantém um objeto com valor nulo, provavelmente como o seguinte
Classe final pública Opcional <t> {// omit ...... estático privado final opcional <?> vazio = novo opcional <> (); private opcional () {this.value = null; } // omita ... public static <t> opcional <t> empty () {@suppresswarnings ("desmarcado") opcional <t> t = (opcional <t>) vazio; retornar t; }} Então, a função de vazio () é retornar o objeto vazio.
Bem, tantos preparativos foram colocados. Pode -se dizer que o OfNullable (Talue T) tem a função e o código -fonte é adicionado.
public static <t> opcional <t> ofnullable (valor t) {retorno valor == null? vazio (): de (valor); } Bem, todos devem entender o que isso significa. A diferença comparada ao (valor t) é que, quando o valor do valor é nulo, de (T valor t) relatará uma nullPointerException; Ofnullable (valor t) não lançará exceção, Ofnullable (valor t) retorna diretamente um objeto vazio.
Isso significa que usamos apenas a função anulável em vez de função em nosso projeto?
Não, se algo existir, então naturalmente terá valor. Quando estamos correndo, não queremos ocultar o NullPointerException. Em vez disso, você precisa relatar imediatamente e, neste caso, usar a função. Mas tenho que admitir que existem realmente poucas cenas. O blogueiro usou apenas essa função na redação de casos de teste Junit.
(2) Orelse (T Outro), Orelseget (fornecedor <? Extende T> Outros) e OrelrelShrow (Fornecedor <? Extende x> Exceptionsupplier)
Essas três funções são memorizadas em um grupo e são chamadas quando o valor passado pelo construtor é nulo. O uso de Orelse e Orelseget é o seguinte. Quando o valor é nulo, um valor padrão é fornecido:
@Testpublic void test () {usuário do usuário = null; usuário = opcional.ofnullable (usuário) .orelse (createUser ()); usuário = opcional.ofnullable (usuário) .orelseget (() -> createUser ()); } public User CreateUser () {usuário do usuário = new User (); user.setName ("Zhangsan"); Retornar usuário;} A diferença entre essas duas funções: quando o valor do usuário não é nulo, a função Orelse ainda executará o método createUser (), enquanto a função Orelseget não executará o método createUser (), para que você possa testá -lo.
Quanto ao ORRELSETHROW, quando o valor é nulo, uma exceção é expulsa diretamente. O uso é o seguinte
Usuário do usuário = null; opcional.ofnullable (Usuário) .orelsethrow (()-> nova exceção ("Usuário não existe"));(3) mapa (função <? Super t ,? estende u> mapper) e plangmap (função <? Super t, opcional <u>> mapeador)
Essas duas funções são colocadas em um conjunto de memória, e essas duas funções fazem a operação da conversão de valores.
Faça o upload diretamente o código -fonte
classe final public final opcional <t> {// omit ...... public <u> opcional <u> map (function <? super t ,? estende u> mapper) {objects.requiirOnNonull (mapper); if (! ispresent ()) retorna vazio (); else {return opcional.ofnullable (mapper.apply (value)); }} // omita ... public <u> opcional <u> Flatmap (função <? Super t, opcional <u>> mapper) {objects.requiirononnull (mapper); if (! ispresent ()) retorna vazio (); else {return Objects.RequirenOnNull (mapper.apply (valor)); }}} Não há diferença entre essas duas funções no corpo da função. A única diferença é o parâmetro de entrada. O tipo de parâmetro de entrada aceito pela função do mapa é função <?? Super T ,? estende u>, enquanto o tipo de entrada do tipo de flapmap é função <??? Super T, opcional <u>>.
Em termos de uso específico, para mapa:
Se a estrutura do usuário for a seguinte
Public class Usuário {Nome da String Private; public String getName () {Return Name; }}Neste momento, o método de escrita de assumir o nome é o seguinte
String city = opcional.ofnullable (usuário) .map (u-> u.getName ()). Get ();
Para Flatmap:
Se a estrutura do usuário for a seguinte
Public class Usuário {Nome da String Private; public opcional <string> getName () {return opcional.ofnullable (nome); }}Neste momento, o método de escrita de assumir o nome é o seguinte
String city = opcional.ofnullable (usuário) .flatmap (u-> u.getName ()). Get ();
(4) Ispresent () e ifpresent (consumidor <? Super t> consumidor)
Coloque essas duas funções e memorize -as. Ispresent Means para determinar se o valor está vazio e o Ifpresent significa fazer algumas operações quando o valor não estiver vazio. Os códigos de origem dessas duas funções são os seguintes
classe final public final opcional <t> {// omit ...... public boolean isPresent () {return value! = null; } // omita ... public void ifpresent (consumidor <? Super t> consumer) {if (value! = Null) consumer.accept (valor); }}Precisa de explicações adicionais, não
if (usuário! = null) {// TODO: faça algo}Escrito
Usuário do usuário = opcional.ofnullable (Usuário); if (opcional.ispresent ()) {// TODO: faça algo} Por esse motivo, a estrutura do código ainda é feia. O blogueiro dará o método de escrita correto mais tarde
Quanto ao ifpresent (consumidor <? Super t> consumidor), o uso também é muito simples, como mostrado abaixo
Opcional.ofnullable (usuário) .IFPRESSE (u-> {// TODO: faça algo});(5) Filtro (predicado <? Super T> predize)
Sem mais delongas, basta fazer o upload do código -fonte
Classe final pública Opcional <t> {// omit ...... objetos. if (! ispresent ()) devolver isso; else Return previc.test (valor)? isto: vazio ();} O método do filtro aceita um predicado para filtrar os valores contidos opcionais. Se os valores incluídos atenderem às condições, o opcional ainda será devolvido; Caso contrário, o opcional.Empty será devolvido.
O uso é o seguinte
Opcional <suser> user1 = opcional.ofnullable (usuário) .Filter (u -> u.getName (). Length () <6);
Como mostrado acima, se o comprimento do nome do usuário for menor que 6, retorne. Se for maior que 6, um objeto vazio será retornado.
Uso prático
Exemplo 1
No método da função
Escrita anterior
public String getCity (usuário do usuário) lança a exceção {if (user! = null) {if (user.getAddress ()! = null) {endereço de endereço = user.getAddress (); if (endereço.getCity ()! = null) {return endereço.getCity (); }}} lança a nova exposição ("Erro de valor"); }Método de escrita Java8
public String getCity (usuário do usuário) lança Exceção {return opcional.ofnullable (usuário) .map (u-> u.getAddress ()) .map (a-> a.getCity ()) .orelSethrow (()-> nova exceção ("Erro de fetch");}Exemplo 2
Por exemplo, no programa principal
Escrita anterior
if (usuário! = null) {doSomething (usuário);}Método de escrita Java8
Opcional.ofnullable (usuário) .IFPRESSE (u-> {Dosomething (u);});Exemplo 3
Escrita anterior
Usuário público getUser (usuário do usuário) lança a exceção {if (user! = null) {string name = user.getName (); if ("zhangsan" .equals (nome)) {return user; }} else {user = new user (); user.setName ("Zhangsan"); devolver usuário; }}Método de escrita Java8
Usuário público getUser (usuário do usuário) {return opcional.ofnullable (usuário) .Filter (u-> "zhangsan" .equals (u.getName ())) .orelseget (()-> {user user1 = new user (); user1.setName ("zhangsan"); retorno1;Outros exemplos não serão listados um por um. No entanto, o blogueiro acredita que o uso dessa programação de cadeia é realmente elegante no código. No entanto, a lógica não é tão óbvia e a legibilidade é reduzida. Nós o usamos de acordo com a situação do projeto.
Resumir
O acima é o conteúdo inteiro deste artigo. Espero que o conteúdo deste artigo tenha certo valor de referência para o estudo ou trabalho de todos. Se você tiver alguma dúvida, pode deixar uma mensagem para se comunicar. Obrigado pelo seu apoio ao wulin.com.