views:

38

answers:

2

Update: this is more-or-less a dupe, and it turns out to be compiler magic adding a constructor to pass in the local variable in build2.

Given an interface like such:

public interface IFoo { public int get(); }

The code below prints 1, 1, 2 and then throws an exception when trying to call getClass().newInstance() on the value returned by build2, but does not when calling the same on the returned value of build1. Any ideas why?

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

My debugger indicates that in the newInstance() call, getConstructor0 is throwing a NoSuchMethodException.

A: 

Right, interfaces don't have constructors; neither default nor any other kind.

duffymo
That's not the point (the first one works).
David
Yes, it is the point. The first one isn't invoking a default constructor. I get it; you don't.
duffymo
@duffymo: Yes, interfaces have no constructors, but concrete classes implementing said interfaces do. In OP's original snippet, the anonymous class created in `build1` has a nullary constructor. The one in `build2` doesn't. Not only do these anonymous classes have constructors, they also have concrete implementations of `get` method (which the `interface` itself of course does not have).
polygenelubricants
Yes, but what's being invoked when the OP calls newInstance on a reference of type interface? (Wait for it....)
duffymo
@duffymo: `newInstance` is invoked on `Class` instances. `IFoo$1` and `IFoo$2` are anonymous but concrete classes implementing `IFoo`. If you start doing `getClass().getInterfaces()`, then of course you can get to the `Class` representing the `interface` (which has no constructor). OP is not trying to instantiate an `interface` (which of course makes no sense as you point out). OP is trying to instantiate anonymous classes (which happens to implement an `interface`). OP is succesful in one attempt, but not the other. My answer explains why.
polygenelubricants
+1  A: 

Here's what happens:

  • newInstance() requires a nullary constructor
  • when you create an anonymous class that is accessing a final variable, a field is actually implicitly created to hold this value, which is initially passed to its implicit constructor
  • thus, the IFoo created in build2 does NOT actually have a nullary constructor

Here's a snippet to show what's going on:

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

It shows that Foo$1 (the assigned binary name for the anonymous IFoo class) has only one constructor, and it takes an int. This is how it can return v, because what's returned is actually whatever is assigned to the implicitly created field by this implicitly created constructor.

It is instructive to decompile the Foo$1 (using e.g. javap -c) to see what bytecode gets generated. You will see that in fact this is what happens when a final variable is accessed by an anonymous class.

Related questions

polygenelubricants
+1 Exactly. Thanks.
David