Domanda

Aggiornamento:questo è più o meno un dupe, e si scopre di essere un compilatore magia aggiunta di un costruttore di passare la variabile locale in build2.

Date un'interfaccia come tale:

public interface IFoo { public int get(); }

Il codice riportato di seguito stampa 1, 1, 2 e quindi genera un'eccezione durante il tentativo di chiamata getClass().newInstance() il valore restituito da build2, ma non quando chiamando lo stesso il valore restituito di build1.Tutte le idee perché?

public class Foo {

 public static IFoo build1() {
  return new IFoo() { public int get() { return 1; } };
 }

 public static IFoo build2(final int v) {
  return new IFoo() { public int get() {return v;} };
 }

 public static void main(String[] args) throws Exception {
  IFoo foo, bar;

  foo = build1();
  System.out.println(foo.get());

  bar = foo.getClass().newInstance();  
  System.out.println(bar.get());

  foo = build2(2);
  System.out.println(foo.get());

  bar = foo.getClass().newInstance();  
  System.out.println(bar.get());
 }
}

Il mio debugger indica che nel newInstance() chiamata, getConstructor0 è il lancio di un NoSuchMethodException.

È stato utile?

Soluzione

Ecco cosa succede:

  • newInstance() richiede un nullary costruttore
  • quando si crea una classe anonima che accede a un final variabile, un campo è in realtà creato implicitamente a tenere questo valore, che è inizialmente superato la sua implicita costruttore
  • così, il IFoo creato nel build2 in realtà NON hanno un nullary costruttore

Ecco un frammento di mostrare ciò che sta succedendo:

import java.lang.reflect.*;
public class Foo {
    interface IFoo { public int get(); }

    public static IFoo build2(final int v) {
        return new IFoo() { public int get() {return v;} };
    }
    public static void main(String[] args) throws Exception {
        Class<?> klazz = build2(42).getClass();
        for (Constructor<?> c : klazz.getDeclaredConstructors()) {
            System.out.println(c);
        }
        // prints: Foo$1(int)
    }
}

Essa mostra che Foo$1 (assegnato binario nome per l'anonimo IFoo di classe) ha un solo costruttore, e ci vuole un int.Questo è come si può return v, perché ciò che viene restituito è in realtà tutto ciò che è assegnato al creato implicitamente campo da questo creato implicitamente costruttore.

È istruttivo per decompilare il Foo$1 (utilizzando, ad esempio, javap -c) per vedere cosa bytecode viene generato.Vedrete che, in realtà, questo è ciò che accade quando un final variabile è accessibile da una classe anonima.

Le domande relative

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top