2009-12-23 11 views
6

S'il vous plaît jeter un oeil à code suivant:classes anonymes internes Méthodes intérieur

import java.util.ArrayList; 
import java.util.List; 

class Main{ 
    public static <T> List<T> modifiedList(final List<T> list){ 
     return new ArrayList<T>(){ 
      @Override 
      public boolean add(T element){ 
       super.add(element); 
       return list.add(element); 
      } 
     }; 
    } 

    public static void main(String[] args) { 
     List<String> originalList=new ArrayList<String>(); 
     List<String> duplicateList=modifiedList(originalList); 
     originalList.add("1"); 
     originalList.add("2"); 
     originalList.add("3"); 
     System.out.println(originalList+" "+duplicateList); 
     duplicateList.add("4"); 
     duplicateList.add("5"); 
     duplicateList.add("6"); 
     System.out.println(originalList+" "+duplicateList); 
    } 

Dans le code ci-dessus, l'instance d'une classe interne anonyme déclarée dans la méthode modifiedList() est en mesure d'accéder au paramètre passé à cette méthode. AFAIK Java crée un fichier bytecode séparé pour les classes internes. Est-ce que quelqu'un peut expliquer comment ces liaisons de variables locales sont gérées par Java au niveau du bytecode? Je veux dire, comment Java garde-t-il exactement la référence à l'objet passé en paramètre à cette méthode?

Toute aide serait grandement appréciée!

[Désolé pour mon pauvre anglais! Si vous comprenez ma question, veuillez modifier ce post et supprimer les erreurs grammaticales. Merci!]

+0

Votre anglais est plutôt bon. J'ai seulement fait quelques petits changements grammaticaux. :) – Amber

Répondre

9

Essentiellement, le code est réécrite par le complier que (notez que je ne l'ai pas essayé de le compiler ..., pourrait avoir des erreurs):

class Main$1<T> 
    extends ArrayList<T> 
{ 
    private final List<T> list; 

    Main$1(final List<T> a) 
    { 
     list = a; 
    } 

    @Override 
    public boolean add(T element) 
    { 
     super.add(element); 
     return list.add(element); 
    } 
} 

et

class Main{ 
    public static <T> List<T> modifiedList(final List<T> list) 
    { 
     return new Main$1<T>(list); 
    } 

    public static void main(String[] args) 
    { 
     List<String> originalList=new ArrayList<String>(); 
     List<String> duplicateList=modifiedList(originalList); 
     originalList.add("1"); 
     originalList.add("2"); 
     originalList.add("3"); 
     System.out.println(originalList+" "+duplicateList); 
     duplicateList.add("4"); 
     duplicateList.add("5"); 
     duplicateList.add("6"); 
     System.out.println(originalList+" "+duplicateList); 
    } 
+0

C'est pourquoi le compilateur a besoin d'être déclaré final. Pour s'assurer que la valeur ne change jamais, la méthode à l'intérieur de la classe interne peut accéder à la valeur copiée-jamais-changée et savoir que c'est la sémantique correcte. S'il n'était pas définitif, il ne devrait pas savoir si la valeur a changé entre la création de l'objet interne et l'invocation de la méthode de l'objet interne. – helios

5
import java.util.ArrayList; 
import java.util.List; 

class Main{ 
    public static <T> List<T> modifiedList(final List<T> list){ 
     return new ArrayList<T>(){ 

      private List<T> originalList=list; 

      @Override 
      public boolean add(T element){ 
       super.add(element); 
       return originalList.add(element); 
      } 
     }; 
    } 

    public static void main(String[] args) { 
     List<String> originalList=new ArrayList<String>(); 
     List<String> duplicateList=modifiedList(originalList); 
     originalList.add("1"); 
     originalList.add("2"); 
     originalList.add("3"); 
     System.out.println(originalList+" "+duplicateList); 
     duplicateList.add("4"); 
     duplicateList.add("5"); 
     duplicateList.add("6"); 
     System.out.println(originalList+" "+duplicateList);  
    } 
} 

Java permet une chose si étrange juste pour faciliter les choses pour les programmeurs. Les deux codes sont sémantiquement identiques et se réduisent au même type de bytecode.