2010-07-14 8 views
9

Comment puis-je obtenir la valeur de userId passée à cette méthode dans ma sous-classe interne anonyme ici?Java: Classe interne anonyme utilisant une variable locale

public void doStuff(String userID) { 
    doOtherStuff(userID, new SuccessDelegate() { 
     @Override 
     public void onSuccess() { 
      Log.e(TAG, "Called delegate!!!! "+ userID); 
     } 
    }); 
} 

Je reçois cette erreur:

Cannot refer to a non-final variable userID inside an inner class defined in a different method

Je suis sûr que je ne peux pas l'affecter comme définitive car il est une variable avec une valeur inconnue. J'avais entendu dire que cette syntaxe préservait la portée d'une certaine façon, donc je pense qu'il doit y avoir une astuce de syntaxe que je ne connais pas encore.

+0

trop mauvais java nécessite final ici. il est évident de compiler que la variable locale n'est pas changée, il ne faut pas demander au codeur de la confirmer. dans le cas où la classe anon écrit dans la variable locale, un avertissement de compilation suffit. mauvais mauvais choix de conception. – irreputable

+0

duplicata possible de [Pourquoi seules les variables finales sont-elles accessibles en classe anonyme?] (Http://stackoverflow.com/questions/4732544/why-are-only-final-variables-accessible-in-anonymous-class) – vaxquis

Répondre

7

Bien sûr, vous pouvez l'affecter comme final - il suffit de mettre ce mot-clé dans la déclaration du paramètre:

public void doStuff(final String userID) { 
    ... 

Je ne suis pas sûr de ce que vous vouliez dire à ce sujet étant une variable avec une valeur inconnue; Tout ce dernier moyen est qu'une fois qu'une valeur est affectée à la variable, elle ne peut pas être re. Puisque vous ne modifiez pas la valeur de l'ID utilisateur dans votre méthode, il n'y a aucun problème à la rendre définitive dans ce cas.

+0

Ah ok . Je suppose que je n'ai pas vraiment compris comment fonctionne la déclaration 'final'. Merci. –

1

Quel est le problème avec ce qui en fait final comme dans

public void doStuff (final String userID) 
1

déclarer la méthode

public void doStuff(final String userID) 

La valeur doit être finale afin que le compilateur peut être sûr qu'il ne change pas. Cela signifie que le compilateur peut lier la valeur à la classe interne à tout moment, sans se soucier des mises à jour.

La valeur ne change pas dans votre code, il s'agit donc d'une modification sans risque.

10

Comme tout le monde l'a dit, les variables locales doivent être finales pour être accessibles par une classe interne.

Voici (en gros) pourquoi est ... si vous écrivez le code suivant (réponse longue, mais, au fond, vous pouvez obtenir la version courte :-):

class Main 
{ 
    private static interface Foo 
    { 
     void bar(); 
    } 

    public static void main(String[] args) 
    { 
     final int x; 
     Foo foo; 

     x = 42; 
     foo = new Foo() 
     { 
      public void bar() 
      { 
       System.out.println(x); 
      } 
     }; 

     foo.bar(); 
    } 
} 

le compilateur il se traduit à peu près comme ceci:

class Main 
{ 
    private static interface Foo 
    { 
     void bar(); 
    } 

    public static void main(String[] args) 
    { 
     final int x; 
     Foo foo; 

     x = 42; 

     class $1 
      implements Foo 
     { 
      public void bar() 
      { 
       System.out.println(x); 
      } 
     } 

     foo = new $1(); 
     foo.bar(); 
    } 
} 

puis ceci:

class Main 
{ 
    private static interface Foo 
    { 
     void bar(); 
    } 

    public static void main(String[] args) 
    { 
     final int x; 
     Foo foo; 

     x = 42; 
     foo = new $1(x); 
     foo.bar(); 
    } 

    private static class $1 
     implements Foo 
    { 
     private final int x; 

     $1(int val) 
     { 
      x = val; 
     } 

     public void bar() 
     { 
      System.out.println(x); 
     } 
    } 
} 

et enfin à ceci:

class Main 
{ 
    public static void main(String[] args) 
    { 
     final int x; 
     Main$Foo foo; 

     x = 42; 
     foo = new Main$1(x); 
     foo.bar(); 
    } 
} 

interface Main$Foo 
{ 
    void bar(); 
} 

class Main$1 
    implements Main$Foo 
{ 
    private final int x; 

    Main$1(int val) 
    { 
     x = val; 
    } 

    public void bar() 
    { 
     System.out.println(x); 
    } 
} 

L'important est où il ajoute le constructeur à 1 $. Imaginez si vous pouviez le faire:

class Main 
{ 
    private static interface Foo 
    { 
     void bar(); 
    } 

    public static void main(String[] args) 
    { 
     int x; 
     Foo foo; 

     x = 42; 
     foo = new Foo() 
     { 
      public void bar() 
      { 
       System.out.println(x); 
      } 
     }; 

     x = 1; 

     foo.bar(); 
    } 
} 

que vous attendez que foo.bar() serait imprimer 1, mais il fait imprimer 42. En exigeant des variables locales à être définitive cette situation confuse ne peut pas se produire.

+0

Question: La classe interne de l'annonce ne fait que des copies automatiques des variables locales 'final' qu'elle utilise correctement? Pas de copies d'autres variables locales (finales ou non) qu'il n'utilise pas directement? Je demande parce que je suis préoccupé par les fuites de mémoire. –

1

En Java 8, cela a un peu changé. Vous pouvez maintenant accéder aux variables effectivement finales.Extrait pertinent et exemple du Oracle documentation (emphase mienne):

However, starting in Java SE 8, a local class can access local variables and parameters of the enclosing block that are final or effectively final. A variable or parameter whose value is never changed after it is initialized is effectively final. For example, suppose that the variable numberLength is not declared final, and you add the highlighted assignment statement in the PhoneNumber constructor:

PhoneNumber(String phoneNumber) { 
    numberLength = 7; // From Kobit: this would be the highlighted line 
    String currentNumber = phoneNumber.replaceAll(
     regularExpression, ""); 
    if (currentNumber.length() == numberLength) 
     formattedPhoneNumber = currentNumber; 
    else 
     formattedPhoneNumber = null; 
} 

Because of this assignment statement, the variable numberLength is not effectively final anymore. As a result, the Java compiler generates an error message similar to "local variables referenced from an inner class must be final or effectively final" where the inner class PhoneNumber tries to access the numberLength variable:

if (currentNumber.length() == numberLength) 

Starting in Java SE 8, if you declare the local class in a method, it can access the method's parameters. For example, you can define the following method in the PhoneNumber local class:

public void printOriginalNumbers() { 
    System.out.println("Original numbers are " + phoneNumber1 + 
     " and " + phoneNumber2); 
} 

The method printOriginalNumbers accesses the parameters phoneNumber1 and phoneNumber2 of the method validatePhoneNumber

Questions connexes