Question

Mise à jour : ce qui est plus ou moins un duper, et il se révèle être compilateur magique ajouter un constructeur à passer dans la variable locale build2.

Étant donné une interface comme par exemple:

public interface IFoo { public int get(); }

Le code ci-dessous, 1 imprime 1, 2 et jette une exception lorsque vous essayez d'appeler getClass (). NewInstance () sur la valeur retournée par build2, mais ne pas lorsque vous appelez la même chose sur la valeur retournée de build1. Toutes les idées pourquoi?

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

Mon débogueur indique que dans l'appel newInstance (), getConstructor0 jette un NoSuchMethodException.

Était-ce utile?

La solution

Voici ce qui se passe:

  • newInstance() nécessite un constructeur arité
  • lorsque vous créez une classe anonyme qui accède à une variable final, un champ est en fait implicitement créé pour maintenir cette valeur, qui est d'abord passé à son constructeur implicite
  • ainsi, le IFoo créé build2 ne fait pas un constructeur arité

Voici un extrait de montrer ce qui se passe:

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

Il montre que Foo$1 (le nom binaire attribué à la classe IFoo anonyme) n'a qu'un seul constructeur, et il faut un int. Voici comment cela peut return v, parce que ce qui est retourné est en fait tout ce qui est attribué au champ créé implicitement par ce constructeur implicitement créé.

Il est instructif de décompiler le Foo$1 (en utilisant par exemple javap -c) pour voir ce que bytecode est généré. Vous verrez que, en fait, ce qui se passe quand une variable final est accessible par une classe anonyme.

Questions connexes

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top