English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية
A primeira vez que entrei em contato com a expressão Lambda foi no TypeScript (superconjunto do JavaScript), na época, foi para que o método this do TypeScript fosse usado fora, não dentro, do método. Após usá-lo, pensei que a Lambda não era do JDK8é uma nova característica importante? Então senti que consultei informações relacionadas e anotei:
I. Parametrização de comportamento
A parametrização de comportamento, simplesmente falando, é a parte principal da função que contém código genérico de classe de modelo, enquanto algumas lógicas que mudam com o cenário de negócios são passadas para a função como parâmetros. A parametrização de comportamento torna o programa mais genérico, para lidar com necessidades frequentemente alteradas.
Considere um cenário de negócios, supondo que precisemos filtrar maçãs através de um programa, primeiramente definimos uma entidade de maçã:
public class Apple { /** número */ private long id; /** cor */ private Color color; /** Peso */ private float weight; /** Local de origem */ private String origin; public Apple() { } public Apple(long id, Color color, float weight, String origin) { this.id = id; this.color = color; this.weight = weight; this.origin = origin; } // Omitir getter e setter }
A demanda inicial do usuário pode ser simplesmente a de poder filtrar apples verdes através do programa, então podemos implementar rapidamente através do programa:
public static List<Apple> filterGreenApples(List<Apple> apples) { List<Apple> filterApples = new ArrayList<>(); for (final Apple apple : apples) { if (Color.GREEN.equals(apple.getColor())) { filterApples.add(apple); } } return filterApples; }
Este código é muito simples, não há muito o que dizer. Mas, quando a demanda do usuário muda para verde, parece que a modificação do código é simples, apenas substituindo a condição de decisão verde pela vermelha. Mas precisamos considerar outro problema, o que fazer se as condições de mudança forem frequentes?63;Se for apenas a mudança de cor, então é bom permitir que o usuário passe a condição de decisão de cor, a qual passa a ser o parâmetro do método de decisão "o conjunto a ser avaliado e a cor a ser filtrada". Mas e se o usuário não quiser apenas avaliar a cor, mas também o peso, o tamanho e outros fatores? Você acha que adicionando diferentes parâmetros a cada vez para completar a decisão é uma boa solução? Mas, quando há cada vez mais condições de filtragem e a combinação de padrões se torna mais complexa, não precisamos considerar todas as situações e ter estratégias correspondentes para cada uma?63;Neste momento, podemos parametrizar a ação, extrair as condições de filtragem e passá-las como parâmetros. Neste caso, podemos encapsular uma interface de decisão:
public interface AppleFilter { /** * Abstração de condição de filtragem * * @param apple * @return */ boolean accept(Apple apple); } /** * Encapsulate the filtering conditions into an interface * * @param apples * @param filter * @return */ public static List<Apple> filterApplesByAppleFilter(List<Apple> apples, AppleFilter filter) { List<Apple> filterApples = new ArrayList<>(); for (final Apple apple : apples) { if (filter.accept(apple)) { filterApples.add(apple); } } return filterApples; }
Após a abstração de comportamento acima, podemos definir as condições de filtragem no local de chamada e passar as condições como parâmetros para o método, neste caso, usando o método da classe anônima:
public static void main(String[] args) { List<Apple> apples = new ArrayList<>(); // Filtrar maçãs List<Apple> filterApples = filterApplesByAppleFilter(apples, new AppleFilter() { @Override public boolean accept(Apple apple) { // filtrar peso maior que10maçã vermelha de 0g return Color.RED.equals(apple.getColor()) && apple.getWeight() > 100; } }); }
Este design é frequentemente usado internamente no JDK, por exemplo, Java.util.Comparator, java.util.concurrent.Callable, etc. Quando usamos esses tipos de interfaces, podemos especificar a lógica de execução da função específica usando classes anônimas no local de chamada, mas, conforme o bloco de código acima, embora seja muito geek, não é suficiente conciso no java8Nós podemos simplificar usando a lambda:
// Filtrar maçãs List<Apple> filterApples = filterApplesByAppleFilter(apples, (Apple apple) -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100); //()->xxx () dentro é o parâmetro do método, xxx é a implementação do método
Definição dois. expressão lambda
Podemos definir a expressão lambda como uma função anônima concisa e passável, primeiramente precisamos entender que a expressão lambda essencialmente é uma função, embora não pertença a nenhum tipo específico, possui lista de parâmetros, corpo da função, tipo de retorno e pode lançar exceções; em segundo lugar, é anônima, a expressão lambda não tem um nome de função; a expressão lambda pode ser passada como um parâmetro, o que simplifica significativamente a escrita do código. A definição do formato é a seguinte:
Formato um: lista de parâmetros -> expressão
Formato dois: lista de parâmetros -> {conjunto de expressões}
É importante notar que a expressão lambda implícita contém a palavra-chave return, portanto, não é necessário escrever explicitamente a palavra-chave return em uma expressão singular, mas quando a expressão é um conjunto de instruções, é necessário adicioná-la explicitamente e usar chaves { } para agrupar várias expressões. Vamos ver alguns exemplos a seguir:
//Retorna o comprimento da string fornecida, implícito return statement (String s) -> s.length() // Sempre retorna42método sem parâmetros () -> 42 // Se houver expressões em várias linhas, use chaves para delimitá-las (int x, int y) -> { int z = x * y; return x + z; }
III. Uso de expressões lambda com base em interfaces funcionais
O uso de expressões lambda requer interfaces funcionais, o que significa que apenas onde há interfaces funcionais podemos simplificar usando expressões lambda.
Interface funcional personalizada:
A interface funcional é definida como uma interface que possui apenas um método abstrato. java8A melhoria na definição da interface é a introdução de métodos padrão, que permitem que forneçamos uma implementação padrão para métodos na interface. No entanto, independentemente do número de métodos padrão, se houver apenas um método abstrato, então é uma interface funcional, conforme mostrado acima (AppleFilter).
/** * Interface de filtragem de maçã */ @FunctionalInterface public interface AppleFilter { /** * Abstração de condição de filtragem * * @param apple * @return */ boolean accept(Apple apple); }
AppleFilter contém apenas um método abstrato accept(Apple apple), conforme definido, pode ser considerado uma interface funcional. Ao definir essa interface, adicionamos a anotação @FunctionalInterface para marcar que essa interface é funcional. Embora isso seja opcional, ao adicionar essa interface, o compilador limita a interface a ter apenas um método abstrato, caso contrário, será gerado um erro. Portanto, é recomendável adicionar essa anotação às interfaces funcionais.
Interfaces funcionais do JDK:
O JDK já possui uma variedade de interfaces funcionais embutidas para expressões lambda, aqui estão exemplos de uso de Predicate<T>, Consumer<T> e Function<T, R>.
Predicate:
@FunctionalInterface public interface Predicate<T> { /** * Avalia este predicado sobre o argumento fornecido. * * @param t o argumento de entrada * @return {@code true} se o argumento de entrada correspondente ao predicado * de outra forma {@code false} */ boolean test(T t); }
A função Predicate é semelhante ao AppleFilter acima, verificando os parâmetros fornecidos com base nas condições definidas externamente e retornando o resultado de verificação booleano. A seguir, usamos Predicate para filtrar os elementos da lista:
/** * * @param list * @param predicate * @param <T> * @return */ public <T> List<T> filter(List<T> list, Predicate<T> predicate) { List<T> newList = new ArrayList<T>(); for (final T t : list) { if (predicate.test(t)) { newList.add(t); } } return newList; }
Usar:
demo.filter(list, (String str -> null != str && !str.isEmpty());
Consumer
@FunctionalInterface public interface Consumer<T> { /** * Realiza essa operação no argumento fornecido. * * @param t o argumento de entrada */ void accept(T t); }
Consumer fornece uma função abstrata accept, que recebe parâmetros mas não retorna valor, a seguir, utiliza-se o Consumer para percorrer o conjunto.
/** * Percorre o conjunto, executando comportamento personalizado * * @param list * @param consumer * @param <T> */ public <T> void filter(List<T> list, Consumer<T> consumer) { for (final T t : list) { consumer.accept(t); } }
Utilizando a interface funcional superior, percorre o conjunto de strings e imprime strings não vazias:
demo.filter(list, (String str -> { if (StringUtils.isNotBlank(str)) { System.out.println(str); } });
Function
@FunctionalInterface public interface Function<T, R> { /** * Aplica essa função ao argumento fornecido. * * @param t o argumento da função * @return o resultado da função */ R apply(T t); }
A função Funcation realiza a operação de conversão, a entrada é os dados do tipo T e o retorno é os dados do tipo R, a seguir, utiliza-se a Function para transformar o conjunto:
public <T, R> List<R> filter(List<T> list, Function<T, R> function) { List<R> newList = new ArrayList<R>(); for (final T t : list) { newList.add(function.apply(t)); } return newList; }
Outros:
demo.filter(list, (String str -> Integer.parseInt(str));
Essas interfaces funcionais também fornecem implementações padrão para operações lógicas, serão introduzidas mais tarde no Java8Métodos padrão de interface serão discutidos mais tarde ~
Coisas a serem observadas durante o uso:
Inferência de tipo:
Durante o processo de codificação, às vezes podemos nos perguntar qual interface funcional específica nossa chamada de código está correspondendo, na verdade, o compilador fará a determinação correta com base nos parâmetros, tipo de retorno, tipo de exceção (se houver) etc.
Na chamada específica, em alguns casos, podemos omitir o tipo do parâmetro, o que pode simplificar ainda mais o código:
// Filtrar maçãs List<Apple> filterApples = filterApplesByAppleFilter(apples, (Apple apple) -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100); // Em alguns casos, podemos até omitir o tipo de parâmetro, o compilador julgará corretamente com base no contexto List<Apple> filterApples = filterApplesByAppleFilter(apples, apple -> Color.R > ED.equals(apple.getColor()) && apple.getWeight() >= 100);
Variável local
Em todos os exemplos acima, nossas expressões lambda usam seus parâmetros principais, também podemos usar variáveis locais dentro de uma expressão lambda, por exemplo
int weight = 100; List<Apple> filterApples = filterApplesByAppleFilter(apples, apple -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= weight);:
Neste exemplo, usamos a variável local weight dentro da expressão lambda, mas é necessário que a variável seja explicitamente declarada como final ou efetivamente final ao usar variáveis locais dentro de uma expressão lambda, isso se deve ao fato de que as variáveis locais são armazenadas na pilha, enquanto a expressão lambda é executada em uma thread diferente, quando essa thread tenta acessar a variável local, há a possibilidade de que a variável seja alterada ou recolhida, então usar o modificador final elimina o problema de segurança de thread.
Quatro. Referência de método
Usar referências de método pode simplificar ainda mais o código, às vezes essa simplificação torna o código mais intuitivo, vamos ver um exemplo:
/* ... omitindo a inicialização de apples */ // Adota expressões lambda apples.sort((Apple a, Apple b)}) -> Float.compare(a.getWeight(), b.getWeight())); // Uso de referência de método apples.sort(Comparator.comparing(Apple::getWeight));
A referência de método liga o método pertencente e o método próprio através de ::, e é dividida em três tipos principais:
Método estático
(args) -> ClassName.staticMethod(args)
Converter para
ClassName::staticMethod
Método de instância de parâmetro
(args) -> args.instanceMethod()
Converter para
> ClassName::instanceMethod // ClassName é o tipo de args
Método de instância externo
(args) -> ext.instanceMethod(args)
Converter para
ext::instanceMethod(args)
Referência:
http://www.codeceo.com/article/lambda-de-java-8.html
O que foi mencionado acima é o JDK que o editor apresentou para você.8Nova característica: expressão Lambda, esperamos que ajude a todos. Se tiverem alguma dúvida, deixem um comentário, eu responder-ei o mais rápido possível. Agradecemos também o apoio ao site Tutorial Yell.
Declaração: O conteúdo deste artigo é de origem na Internet, pertencente ao respectivo autor. O conteúdo é contribuído e carregado voluntariamente pelos usuários da Internet. Este site não possui direitos de propriedade, não foi editado manualmente e não assume responsabilidade por eventuais responsabilidades legais. Se você encontrar conteúdo suspeito de violação de direitos autorais, por favor, envie um e-mail para: notice#oldtoolbag.com (ao enviar e-mail, substitua # por @) para denunciar e forneça provas relevantes. Aparentemente, o site deletará imediatamente o conteúdo suspeito de violação de direitos autorais.