2

I was playing around with anonymous subclasses and I found a problem that I can't get around.

EDIT: problem is solved thanks to thomas, the complete working code is on pastebin (Foo.java; FooTest.java)

2 Classes; Foo and FooTest... Code first:

class Foo {

    public Foo () {}

    public void whichClassAmI () {
        System.out.println(this.getClass());                                                 
    }

    public void say () {
        System.out.println("Foo says: nothing");                                             
    }   

    public <T extends Foo> T getAnotherFoo () {                                              

        try {                                                                                
            Class<? extends Foo> c = this.getClass();                                        
            T r = (T)(c.getConstructor().newInstance());                                     
            return r;                                                                        
        } catch (Exception e) {                                                              
            throw new RuntimeException(e);                                                   
        }
    }                                                                                        

}

_

class FooTest {                                                                              

    public static String stuff = "";                                                         

    public static void main (String[] args) {                                                

        Foo f1 = new Foo();                                                                  

        // instance of anon subclass                                                         
        Foo f2 = new Foo(){                                                                  
            public void say () {                                                             
                System.out.println("Modded Foo says: " + stuff);                             
            }                                                                                
        };                                                                                   

        f1.whichClassAmI();                                                                  
        f2.whichClassAmI();                                                                  

        stuff = "stuff";                                                                     
        f1.say();                                                                            
        f2.say();                                                                            

        Foo f3 = f1.getAnotherFoo();                                                         
        f3.say();                                                                            

        Foo f4 = f2.getAnotherFoo(); // <-- exception here
    }

}

So this code compiles with an unsafe operation warning, runs and throws an exception; output is:

Note: Foo.java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
class Foo
class FooTest$1
Foo says: nothing
Modded Foo says: stuff
Foo says: nothing
Exception in thread "main" java.lang.RuntimeException: java.lang.NoSuchMethodException: FooTest$1.<init>()
    at Foo.getAnotherFoo(Foo.java:20)
    at FooTest.main(FooTest.java:23)
Caused by: java.lang.NoSuchMethodException: FooTest$1.<init>()
    at java.lang.Class.getConstructor0(Class.java:2723)
    at java.lang.Class.getConstructor(Class.java:1674)
    at Foo.getAnotherFoo(Foo.java:17)
    ... 1 more

what I don't understand is:

  1. f2 class is FooTest$1 and this class seems to be not extending Foo

  2. if (1) is true why does " Class c = [...] " can be set with FooTest$1

  3. if (1) is false and (2) works correct, why doesn't it find the method?

cdx
  • 99
  • 8

1 Answers1

4

To 1): f2 is of type FooTest$1 which extends Foo but that isn't printed, you just get the current class and no superclasses or interfaces.

To 2): Since 1) is false, there's not question here :)

To 3): The problem is that the annonymous inner class FooTest$1 needs an external FooTest instance to be created. Calling the constructor via reflection would try to create a new instance without an enclosing instance of FooTest and such a method is not available (i.e. a method that creates an instance of an inner class without an instance of the enclosing class).

What you need to do is get the constructor that takes an instance of the enclosing class as its parameter. For more information have a look at this question: Is it possible to create an instance of nested class using Java Reflection?

Edit:

I read your code again and am ashamed I missed this: the FooTest$1 class is actually a static inner class since it is created in the static main method. Thus it doesn't have a constructor taking an enclosing class instance.

However, since you create the class and its constructor inline that constructor isn't publicly visible. Thus getClass().getConstructor() won't return it (this class will only return public constructors. In that case you have to use getClass().getDeclaredConstructor(). The same is true for fields and methods, just for the record.

Community
  • 1
  • 1
Thomas
  • 87,414
  • 12
  • 119
  • 157
  • I read the link you pointed me to, but i dont understand how to adapt this to my anon subclass. getDeclaredClasses doenst return any class (which is obvious since i haven't declared any subclass). also i have modified it the way you told me trying to get the outer instance into the constructor of the Foo class which then told me that "java.lang.NoSuchMethodException: Foo.(Foo)" and when i create that constructor it throws the old exception again. – cdx Jan 19 '12 at 14:42
  • @cdx well, try something like `c.getConstructor(FooTest.class).newInstance(someFooTestInstance)`. This works only if you're creating a `FooTest$1` instance and have access to a `FooTest`. To get the enclosing class try `getClass().getEnclosingClass()`. – Thomas Jan 19 '12 at 14:47
  • @cdx regarding your addition to the comment: the outer class is not `Foo` (which is the super class) but `FooTest` in your case. – Thomas Jan 19 '12 at 14:48
  • i needed to edit the comment because i didnt know that hitting return would already save my comment ^^. these are my changes http://pastebin.com/pFcdEC7N as i tried to get this working, i hope these go in the right direction. i found that it already fails when it comes to find the constructor. FooTest$1 extends Foo, and FooTest is its enclosing class - so FooTest$1 should find the constructor `public Foo (FooTest f) {}`, but keeps telling me it cant `java.lang.NoSuchMethodException: FooTest$1.(FooTest)`. ill keep searching – cdx Jan 19 '12 at 19:50
  • @ctx, I overlooked an important part and updated my answer. Please have a look. – Thomas Jan 19 '12 at 20:48