Amis, comme vous l'avez déjà remarqué, fin juin, nous allons créer de nombreux nouveaux groupes, dont le prochain volet du cours
"Java Developer" , qui a plu à tous. En ce moment, nous partageons avec vous une nouvelle traduction préparée pour les étudiants de ce cours.

Lors de l'ajout de nouvelles fonctionnalités au langage de programmation, les développeurs de langage prêtent généralement attention aux conflits avec les fonctionnalités actuelles du langage, aux changements qui conduisent à une incompatibilité avec les versions précédentes, aux erreurs et à toutes les situations qui peuvent conduire à un comportement indéfini ou inattendu.
Cependant, souvent, ils n'accordent pas assez d'attention aux changements mineurs dans les nouvelles techniques pratiques d'écriture de code. Ces changements sont souvent des effets secondaires de nouvelles fonctionnalités. Les modifications de ce type ne sont pas à proprement parler de nouvelles fonctionnalités. Ce sont des changements subtils qui sont apparus en raison d'autres fonctionnalités ou de leurs combinaisons.
Classes internes anonymes
En Java, les classes internes sont des classes définies comme membres d'une classe. Il existe quatre types de classes internes:
- imbriqué statique
- interne
- local (méthode locale)
- anonyme
Les classes internes anonymes sont des classes sans nom qui fournissent une implémentation d'une classe existante. Ces classes sont largement utilisées pour gérer les événements dans la programmation orientée événements. Habituellement, une classe interne anonyme fournit une implémentation à la volée d'une classe abstraite. Cependant, ce n'est pas nécessaire. Une classe interne anonyme peut être créée à partir d'une classe non abstraite.
Je crois qu'il y a un point qui n'est pas entièrement compris par rapport aux classes internes anonymes. Le fait est que le programmeur
crée en fait
une sous-classe de la classe source . Cette sous-classe porte le nom
Class$X
, où
Class
est la classe externe et
X
est le nombre représentant l'ordre dans lequel les classes internes sont instanciées dans la classe externe. Par exemple,
AnonDemo$3
est la troisième classe interne créée dans
AnonDemo
. Vous ne pouvez pas appeler ces classes de la manière habituelle. Et, contrairement à d'autres types de classes internes, une classe interne anonyme est toujours implicitement un enfant du type à partir duquel elle a été créée (à l'exception de l'utilisation de
var
, que nous verrons bientôt).
Regardons un exemple.
class Anon { }; public class AnonDemo { public static void main (String[] args) { Anon anonInner = new Anon () { public String toString() { return "Overriden"; }; public void doSomething() { System.out.println("Blah"); }; }; System.out.println(anonInner.toString()); anonInner.doSomething();
Dans cet exemple, nous avons instancié une classe interne anonyme basée sur la classe Anon. En substance, nous avons
créé une sous-classe sans nom d'une classe particulière . Avant Java 10, les classes internes anonymes étaient presque toujours implicitement polymorphes. Je dis «presque», car un tel code non polymorphe comme celui-ci, bien sûr, s'exécutera.
new Anon() { public void foo() { System.out.println("Woah"); } }.foo();
Cependant, si nous voulons affecter le résultat de la création d'une instance d'une classe interne anonyme au type d'origine, alors une telle opération sera de nature polymorphe. Les raisons de cela résident dans le fait que nous créons implicitement une sous-classe de la classe que nous avons spécifiée comme source pour la classe interne anonyme et nous ne pourrons pas accéder au type d'objet spécifique (
Class$X
) afin de le spécifier dans le code source.
Polymorphisme et classes internes anonymes, conséquences pratiques
Avez-vous remarqué le code ci-dessus? Puisque nous utilisons une référence de classe de base à un objet de sous-classe, selon les lois du polymorphisme, nous ne pouvons faire référence qu'à 1) des méthodes définies par la classe de base ou 2) des méthodes virtuelles remplacées dans la sous-classe.
Par conséquent, dans l'extrait de code précédent, appeler
toString()
pour un objet de classe interne anonyme nous donnerait une valeur «Overridden» remplacée, cependant, appeler
doSomething()
entraînerait une erreur de compilation. Quelle en est la raison?
Un objet de sous-classe avec une référence au type de la classe de base n'a pas accès aux membres de la sous-classe via cette référence à la classe de base. La seule exception à cette règle est si une sous-classe remplace la méthode de classe de base. Dans ce cas, Java, fidèle à sa nature polymorphe, utilise le Dynamic Method Dispatch pour sélectionner la version de la méthode de la sous-classe virtuelle au moment de l'exécution.
Si vous ne le saviez pas déjà, une
méthode virtuelle est une méthode qui peut être remplacée. En Java, toutes les méthodes non finales, non privées et non statiques sont virtuelles par défaut. Je parle par défaut, et non implicitement, car différents jvm peuvent effectuer des optimisations qui peuvent changer cela.
Qu'est-ce que Java 10 a à voir avec cela?
Une petite fonctionnalité appelée inférence de type. Jetez un œil à l'exemple suivant:
class Anon { }; public class AnonDemo { public static void main(String[] args) { var anonInner = new Anon() { public void hello() { System.out.println( "New method here, and you can easily access me in Java 10!\n" + "The class is: " + this.getClass() ); }; }; anonInner.hello();
Ça marche, on peut appeler
hello()
! Le diable est dans les détails. Si vous connaissez
var
, vous comprenez déjà ce qui se passe ici. En utilisant le nom réservé de type
var
,
Java a pu déterminer le type exact de la classe interne anonyme. Par conséquent, nous ne sommes plus limités à référencer la classe de base pour accéder à l'objet de sous-classe.Qu'avions-nous fait avant Java 10 lorsque nous avions besoin d'une référence à une sous-classe?
Ce n'est un secret pour personne que dans un passé lointain mais non oublié, il y avait un grand débat sur les classes internes. Et si c'est un secret, alors c'est l'un des pires secrets du monde. Sans aucun doute, beaucoup n'approuveront pas le fait que vous ayez besoin d'une référence exacte à une classe interne anonyme, car l'idée est d'éviter d'ajouter des ordures à la classe. Les classes anonymes doivent être utilisées pour exécuter un contrat à la volée, pour une opération liée logiquement à une autre classe, par exemple, pour gérer des événements. Cependant, probablement, la curieuse Barbara n'était pas arrachée de son nez, et je parie que la plupart des développeurs sont très curieux. Peut-être au détriment du bon sens!
Avant Java 10, nous pouvons obtenir un effet similaire en utilisant la réflexion, comme suit:
Anon anonInner2 = new Anon() { public void hello() { System.out.println("Woah! "); }; }; anonInner2.getClass().getMethod("hello").invoke(anonInner2);
Source complète
Peut prendre
ici public class VarAnonInner { public static void main (String[] args) throws Exception { var anonInner = new Anon() { public void hello() { System.out.println("New method here, and you can easily access me in Java 10!\n" + "The class is: " + this.getClass() ); }; }; anonInner.hello(); Anon anonInner2 = new Anon() { public void hello() { System.out.println("Woah! "); }; }; anonInner2.getClass().getMethod("hello").invoke(anonInner2); new Anon() { public void hello() { System.out.println("Woah!!! "); }; }.hello();
Nous attendons vos commentaires et rappelons qu'un
webinaire gratuit sur le cours aura lieu aujourd'hui à 20h00.