English | 简体中文 | 繁體中文 | Русский язык | Français | Español | Português | Deutsch | 日本語 | 한국어 | Italiano | بالعربية

Analyse approfondie de JDK8Nouvelles fonctionnalités: expression Lambda

La première fois que j'ai rencontré les expressions Lambda, c'était dans TypeScript (une superposition de JavaScript), à l'époque, c'était pour que la méthode this de TypeScript soit utilisée en dehors de la méthode et non à l'intérieur. Après utilisation, j'ai soudainement pensé que Lambda n'est pas JDK8c'est une nouvelle fonctionnalité de poids lourd ? Alors, il semble que j'ai consulté des documents pertinents et les ai notés :

I. Paramétrage des comportements

Les paramètres d'opération, en termes simples, c'est-à-dire que le corps de la fonction ne contient que du code générique de la classe modèle, et certaines logiques qui changent avec la scène d'entreprise sont transmises à la fonction sous forme de paramètres. L'utilisation des paramètres d'opération permet à un programme d'être plus générique, pour répondre aux besoins fréquents de modification.

Considérons une scène d'entreprise, supposons que nous devions filtrer des pommes à travers un programme, nous définissons d'abord une entité de pomme :

public class Apple {
/** numéro */
private long id;
/** couleur */
private Color color;
/** 重量 */
private float weight;
/** 产地 */
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;
}
// Omitted getter and setter
}

The initial requirement of the user may have been just to be able to filter out green apples through the program, so we can quickly implement it through the program:

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;
}

This code is very simple, there is nothing to say. But if the user's requirement changes to green, it looks like changing the code is also very simple, just change the judgment condition from green to red. But we need to consider another problem, what if the change condition changes frequently?? If it's just a change in color, then it's good for us to let the user pass in the judgment condition for color directly, and the parameter of the judgment method changes to "the set to be judged and the color to be filtered". But what if the user wants to judge not only color but also weight, size, and so on? Do you think it's okay to add different parameters one by one to complete the judgment? But is it really good to pass parameters this way? If the filtering conditions become more and more, and the combination pattern becomes more and more complex, do we need to consider all the cases and have corresponding strategies for each case?? At this point, we can parameterize the behavior, extract the filtering conditions and pass them in as parameters. At this time, we can encapsulate a judgment interface out:

public interface AppleFilter {
/**
* Abstracte des conditions de filtrage
*
* @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;
}

Après l'abstraction des comportements ci-dessus, nous pouvons définir les conditions de filtrage à l'endroit de l'appel et transmettre les conditions en tant que paramètres à la méthode, à ce moment-là, nous utilisons la méthode de la classe anonyme :

public static void main(String[] args) {
List<Apple> apples = new ArrayList<>();
// Filtrage des pommes
List<Apple> filterApples = filterApplesByAppleFilter(apples, new AppleFilter() {
@Override
public boolean accept(Apple apple) {
// Filtrer les pommes dont le poids est supérieur à100g de pomme rouge
return Color.RED.equals(apple.getColor()) && apple.getWeight() > 100;
}
});
}

Cette conception est souvent utilisée en interne dans JDK, par exemple Java.util.Comparator, java.util.concurrent.Callable, etc. Lorsque nous utilisons ce type d'interface, nous pouvons spécifier la logique d'exécution de la fonction spécifique à l'endroit de l'appel en utilisant une classe anonyme, mais comme nous pouvons le voir dans le bloc de code ci-dessus, bien que cela soit très geek, cela n'est pas aussi concis que Java8Nous pouvons le simplifier à l'aide de lambda :

// Filtrage des pommes
List<Apple> filterApples = filterApplesByAppleFilter(apples,
(Apple apple) -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100);
//()->xxx () est le paramètre de la méthode, xxx est l'implémentation de la méthode

Deuxième partie : définition de l'expression lambda

Nous pouvons définir l'expression lambda comme une fonction anonyme concise et transmissible. Tout d'abord, nous devons comprendre que l'expression lambda est essentiellement une fonction, bien qu'elle ne appartienne pas à une classe spécifique, elle possède une liste de paramètres, un corps de fonction, un type de retour et peut lever des exceptions ; en second lieu, elle est anonyme, l'expression lambda n'a pas de nom de fonction ; l'expression lambda peut être transmise comme un paramètre, ce qui simplifie grandement la rédaction du code. La définition du format est la suivante :

Format un : liste des paramètres -> Expression

Format deux : liste des paramètres -> {ensemble d'expressions}

Il convient de noter que l'expression lambda implique la clé return, donc dans une expression unique, nous n'avons pas besoin d'écrire explicitement la clé return, mais lorsqu'une expression est un ensemble d'instructions, il est nécessaire d'ajouter explicitement return, et d'encadrer plusieurs expressions avec des accolades { }. Voici quelques exemples :

//Retourne la longueur de la chaîne donnée, implique une instruction return
(String s) -> s.length() 
// retourne toujours42méthode sans paramètres
() -> 42 
// Si l'expression contient plusieurs lignes, utilisez des accolades pour l'encadrer
(int x, int y) -> {
int z = x * y;
return x + z;
}

Troisième partie : Utilisation des expressions lambda basée sur les interfaces fonctionnelles

L'utilisation des expressions lambda nécessite l'aide des interfaces fonctionnelles, ce qui signifie que seules les interfaces fonctionnelles peuvent être simplifiées par des expressions lambda.

Interface fonctionnelle personnalisée :

L'interface fonctionnelle est définie comme une interface possédant une seule méthode abstraite. java8L'amélioration apportée à la définition de l'interface consiste à introduire des méthodes par défaut, ce qui nous permet de fournir des implémentations par défaut pour les méthodes dans l'interface. Cependant, que ce soit un ou plusieurs méthodes par défaut, tant qu'il y a une et une seule méthode abstraite, il s'agit d'une interface fonctionnelle, comme le montre (référence AppleFilter) :

/**
* Interface de filtrage des pommes
*/
@FunctionalInterface
public interface AppleFilter {
/**
* Abstracte des conditions de filtrage
*
* @param apple
* @return
*/
boolean accept(Apple apple);
}

AppleFilter ne contient qu'une méthode abstraite accept(Apple apple), selon la définition, on peut le considérer comme une interface fonctionnelle. Lors de la définition de cette interface, nous avons ajouté l'annotation @FunctionalInterface pour indiquer que cette interface est une interface fonctionnelle. Bien que cette annotation soit optionnelle, lorsque l'interface est ajoutée, le compilateur restreint l'interface à ne permettre qu'une seule méthode abstraite, sinon il affiche une erreur. Par conséquent, il est recommandé d'ajouter cette annotation aux interfaces fonctionnelles.

Interfaces fonctionnelles incluses dans la JDK :

La JDK inclut déjà des interfaces fonctionnelles riches pour les expressions lambda, voici des exemples d'utilisation de Predicate<T>, Consumer<T> et Function<T, R>.

Predicate:

@FunctionalInterface
public interface Predicate<T> {
/**
* Évalue ce prédicat sur l'argument donné.
*
* @param t l'argument d'entree
* @return {@code true} si l'argument d'entrée correspond au prédicat,
* sinon {@code false}
*/
boolean test(T t);
}

La fonction Predicate est similaire à AppleFilter mentionnée précédemment, elle utilise les conditions définies en externe pour vérifier les paramètres entrants et renvoie le résultat de la vérification boolean. Voici comment utiliser Predicate pour filtrer les éléments d'une liste :

/**
*
* @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;
}

Utilisez :

demo.filter(list, (String str -> null != str && !str.isEmpty());

Consumer

@FunctionalInterface
public interface Consumer<T> {
/**
* Effectue cette operation sur l'argument fourni.
*
* @param t l'argument d'entree
*/
void accept(T t);
}

Consumer fournit une fonction abstraite accept, qui reçoit des paramètres sans retourner de valeur, voici comment utiliser Consumer pour iterer sur l'ensemble.

/**
* Parcourez l'ensemble et executez une action personnalisée
*
* @param list
* @param consumer
* @param <T>
*/
public <T> void filter(List<T> list, Consumer<T> consumer) {
for (final T t : list) {
consumer.accept(t);
}
}

Utilisez l'interface fonctionnelle ci-dessus pour iterer sur le jeu de chaines de caracteres, et imprimez les chaines non vides :

demo.filter(list, (String str -> {
if (StringUtils.isNotBlank(str)) {
System.out.println(str);
}
});

Function

@FunctionalInterface
public interface Function<T, R> {
/**
* Applique cette fonction a l'argument fourni.
*
* @param t l'argument de la fonction
* @return le resultat de la fonction
*/
R apply(T t);
}

La fonction Funcation effectue une operation de transformation, l'entree est des donnees de type T, et le retour est des donnees de type R, voici comment utiliser Function pour transformer le ensemble :

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;
}

Autres :

demo.filter(list, (String str -> Integer.parseInt(str));

Ces interfaces fonctionnelles fournissent également des implémentations par défaut des opérations logiques, nous les présenterons plus tard dans java8Nous parlerons de la méthode par défaut de l'interface plus tard ~

Certaines choses à noter pendant l'utilisation :

Inference de type :

Pendant le processus de codage, il peut arriver que nous nous demandions quel interface fonctionnel spécifique notre code d'appel va correspondre, en fait, le compilateur effectue un jugement correct en fonction des paramètres, du type de retour, du type d'exception (si elle existe) et autres.
Lors de l'appel en particulier, dans certains cas, on peut omettre le type de paramètre, ce qui permet de simplifier davantage le code :

// Filtrage des pommes
List<Apple> filterApples = filterApplesByAppleFilter(apples,
(Apple apple) -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= 100);
// Dans certains cas, nous pouvons même omettre le type de paramètre, le compilateur peut déterminer correctement à partir du contexte
List<Apple> filterApples = filterApplesByAppleFilter(apples,
apple -> Color.R
> ED.equals(apple.getColor()) && apple.getWeight() >= 100);

Variables locales

Dans tous les exemples précédents, nos expressions lambda utilisent leurs paramètres principaux, nous pouvons également utiliser des variables locales dans la lambda, comme suit

int weight = 100;
List<Apple> filterApples = filterApplesByAppleFilter(apples,
apple -> Color.RED.equals(apple.getColor()) && apple.getWeight() >= weight); :

Dans cet exemple, nous utilisons la variable locale weight dans la lambda, mais il est nécessaire de déclarer explicitement la variable locale comme final ou en fait final dans la lambda, principalement parce que les variables locales sont stockées sur le pile, et l'expression lambda s'exécute dans un autre thread, lorsque ce thread tente d'accéder à la variable locale, il existe un risque que la variable soit modifiée ou recyclée, donc après l'ajout de final, il n'y aura pas de problème de sécurité des threads.

Quatre. Appel de méthode

L'utilisation de l'appel de méthode peut simplifier davantage le code, parfois cette simplification rend le code plus intuitif, regardons un exemple :

/* ... Opérations d'initialisation de apples omises */
// Utilisation de l'expression lambda
apples.sort((Apple a, Apple b) -> Float.compare(a.getWeight(), b.getWeight()));
// Utilisation de la référence de méthode
apples.sort(Comparator.comparing(Apple::getWeight));

La référence de méthode relie la méthode appartenant à l'objet et l'objet lui-même à l'aide de ::, et elle est principalement divisée en trois catégories :

méthode statique

(args) -> ClassName.staticMethod(args)

est converti en

ClassName::staticMethod

méthode d'instance des paramètres

(args) -> args.instanceMethod()

est converti en

> ClassName::instanceMethod // ClassName est le type de args

méthode d'instance externe

(args) -> ext.instanceMethod(args)

est converti en

ext::instanceMethod(args)

Référence :

http://www.codeceo.com/article/lambda-de-java-8.html

Ce que j'ai décrit ci-dessus est ce que l'éditeur vous présente JDK8Nouvelle fonctionnalité : l'expression Lambda, j'espère qu'elle vous sera utile. Si vous avez des questions, n'hésitez pas à me laisser un message, l'éditeur répondra à temps. Je tiens également à remercier chaleureusement le soutien du site de tutoriels Yelling !

Déclaration : le contenu de cet article est issu du réseau, la propriété intellectuelle appartient aux auteurs respectifs, le contenu est fourni par les utilisateurs d'Internet de manière spontanée et auto-publiée, ce site ne détient pas de propriété intellectuelle, n'a pas été édité par l'homme, et n'assume aucune responsabilité juridique. Si vous trouvez du contenu présumé de violation de droits d'auteur, veuillez envoyer un e-mail à : notice#oldtoolbag.com (veuillez remplacer # par @ lors de l'envoi d'un e-mail pour signaler une violation, et fournir des preuves pertinentes. Une fois vérifié, ce site supprimera immédiatement le contenu présumé illicite.

Vous pourriez aussi aimer