Lambda cette référence en java

Je veux convertir une anonymous class en une lambda expression . Mais cette classe anonyme j’utilise le mot this clé this .

Par exemple, j’ai écrit ce simple modèle Observer/Observable :

 import java.util.ArrayList; import java.util.Collection; public static class Observable { private final Collection notifiables = new ArrayList(); public Observable() { } public void addObserver(Observer notifiable) { notifiables.add(notifiable); } public void removeObserver(Observer notifiable) { notifiables.add(notifiable); } public void change() { notifiables.forEach(notifiable -> notifiable.changed(this)); } } public interface Observer { void changed(Observable notifier); } 

et cet exemple de code avec une classe anonyme (utilisez le mot-clé this):

 public class Main { public static void main(Ssortingng[] args) { Observable observable = new Observable(); observable.addObserver(new Observer() { @Override public void changed(Observable notifier) { notifier.removeObserver(this); } }); observable.change(); } } 

mais quand je le convertis en une expression lambda:

 public class Main { public static void main(Ssortingng[] args) { Observable observable = new Observable(); observable.addObserver(notifier -> { notifier.removeObserver(this); }); observable.change(); } } 

J’ai cette erreur de compilation:

 Cannot use this in a static context and in a non `static` context public class Main { public void main(Ssortingng[] args) { method(); } private void method() { Observable observable = new Observable(); observable.addObserver(notifier -> { notifier.removeObserver(this); }); observable.change(); } } 

l’erreur de compilation est la suivante:

 The method removeObserver(Main.Observer) in the type Main.Observable is not applicable for the arguments (Main) 

Donc, ma question est la suivante: y a-t-il un moyen de faire référence à l’object “lambda” avec this ?

Vous ne pouvez pas vous y référer dans une expression lambda. La sémantique de this a été modifiée pour faire référence à l’instance de la classe environnante uniquement, à partir de la lambda. Il n’y a aucun moyen de faire référence à l’expression lambda à l’intérieur du lambda.

Le problème est que vous utilisez this dans la méthode main() . La méthode principale est statique et il n’y a aucune référence à un object qui le représente.

Lorsque vous utilisez this dans une instance d’une classe interne, vous faites référence à l’instance de la classe interne. Une expression lambda n’est pas une classe interne, this ne fait pas référence à l’instance de l’expression lambda. Il fait référence à l’instance de la classe dans laquelle vous définissez l’expression lambda. Dans votre cas, il s’agirait d’une instance de Main. Mais comme vous êtes dans une méthode statique, il n’y a pas d’instance.

C’est ce que vous dit votre seconde erreur de compilation. Vous remettez une instance de Main à votre méthode. Mais votre signature de méthode nécessite une instance de Observer.

Mettre à jour:

La spécification de langage Java 15.27.2 indique :

Contrairement au code apparaissant dans les déclarations de classe anonymes, la signification des noms et des mots-clés this et super apparaissant dans un corps lambda, ainsi que l’accessibilité des déclarations référencées, sont identiques à celles du contexte (sauf que les parameters lambda introduisent de nouveaux noms).

La transparence de ceci (à la fois explicite et implicite) dans le corps d’une expression lambda – c’est-à-dire la traiter de la même manière que dans le contexte environnant – permet plus de flexibilité pour les implémentations et empêche la signification de noms non qualifiés dans le corps sur la résolution de surcharge.

En pratique, il est rare qu’une expression lambda ait besoin de parler d’elle-même (soit pour s’appeler de manière récursive, soit pour invoquer ses autres méthodes), alors qu’il est plus courant d’utiliser des noms pour désigner des objects de la classe sinon être ombré ( ceci, toSsortingng () ). S’il est nécessaire qu’une expression lambda se réfère à elle-même (comme si via this ), une référence de méthode ou une classe interne anonyme doit être utilisée à la place.

Solution de contournement 1

Votre méthode change() jette quand même ConcurrentModificationException .

 public class Main { public static void main(Ssortingng[] args) { Observable observable = new Observable(); final Observer[] a = new Observer[1]; final Observer o = er -> er.removeObserver(a[0]); // !! a[0] = o; observable.addObserver(o); observable.change(); } } public class Observable { private final java.util.Collection n = java.util.new ArrayList<>(); public void addObserver(Observer notifiable) { n.add(notifiable); } public void removeObserver(Observer notifiable) { n.add(notifiable); } public void change() { for (final Observer o : n.toArray(new Observer[n.size()])) { o.changed(this); } } } public interface Observer { void changed(Observable notifier); } 

Solution de contournement 2

J’ai changé le changed(Observable) pour le changed(Observable, Observer) afin qu’un observateur puisse se manipuler.

 public class Main { public static void main(Ssortingng[] args) { Observable observable = new Observable(); final Observer o = (er, ee) -> er.removeObserver(ee); // !! observable.addObserver(o); observable.change(); } } public class Observable { private final java.util.Collection n = new java.util.ArrayList<>(); public void addObserver(Observer notifiable) { n.add(notifiable); } public void removeObserver(Observer notifiable) { n.add(notifiable); } public void change() { for (final Observer o : n.toArray(new Observer[n.size()])) { o.changed(this, o); } } } public interface Observer { void changed(Observable notifier, Observer notifiee); }