2010-07-14 25 views
9

¿Cómo puedo obtener el valor de userId pasado a este método en mi subclase interna anónima aquí?Java: clase interna anónima usando una variable local

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

me sale este error:

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

Estoy bastante seguro de que no puedo asignarle como definitivo, ya que es una variable con un valor desconocido. Había escuchado que esta sintaxis conserva el alcance de alguna manera, así que creo que debe haber un truco de sintaxis que todavía no conozco.

+0

lástima Java requiere última aquí. es obvio para el compilador que la variable local no se cambia, no debe pedirle al codificador que lo confirme. en caso de que la clase anon escriba en la variable local, una advertencia de compilación es suficiente. mala mala elección de diseño. – irreputable

+0

posible duplicado de [¿Por qué solo las variables finales son accesibles en la clase anónima?] (Http://stackoverflow.com/questions/4732544/why-are-only-final-variables-accessible-in-anonymous-class) – vaxquis

Respuesta

7

Claro que se puede asignar como final - sólo hay que poner la palabra clave en la declaración del parámetro:

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

No estoy seguro de lo que quería decir acerca de que sea una variable con un valor desconocido; todo lo que significa finalmente es que una vez que se asigna un valor a la variable, no puede ser re-asignado. Como no está cambiando el valor del ID de usuario dentro de su método, no hay problema para que sea definitivo en este caso.

+0

Ah ok . Supongo que realmente no entendí cómo funciona la declaración 'final'. Gracias. –

1

Cuál es el problema con lo que es final como en

public void doStuff (final String userID) 
1

declarar el método

public void doStuff(final String userID) 

El valor tiene que ser definitiva para que el compilador puede estar seguro de que no cambia. Esto significa que el compilador puede vincular el valor a la clase interna en cualquier momento, sin preocuparse por las actualizaciones.

El valor no cambia en su código, por lo que este es un cambio seguro.

10

Como todos los demás aquí han dicho, las variables locales tienen que ser definitivas para acceder a una clase interna.

Aquí es (básicamente) por qué es así ... si se escribe el siguiente código (respuesta larga, pero, en el fondo, se puede obtener la versión corta :-):

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

el compilador lo traduce más o menos así:

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

y luego esto:

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

y finalmente a esto:

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

Lo importante es que agrega el constructor a $ 1. Imagínese si usted puede hacer esto:

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 se puede esperar que foo.bar() imprimiría 1 pero sería realmente imprimir 42. Al exigir que las variables locales a ser definitiva esta confusa situación no puede surgir.

+0

Pregunta: ¿La clase interna annon solo hace copias automáticas de las variables locales 'finales' que usa correctas? ¿No copias de otras variables locales (finales o no) que no usa directamente? Lo pregunto porque me preocupan las filtraciones de memoria. –

1

En Java 8, esto ha cambiado un poco. Ahora puede acceder a las variables que son efectivamente final.fragmento relevante y el ejemplo de la (el énfasis es mío) Oracle documentation:

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

Cuestiones relacionadas