Monday, August 6, 2007

Java research: Anonymous Inner Classes

There are a lot of articles through Internet which have mistakes regarding anonymous inner classes in Java. Anonymous inner class:

  • has no name;
  • can’t be declared as static;
  • can be instantiated only once.

Let me show you the truth.
Consider the following code:

public class Anonymous {
public static void main(String[] args) {
Runnable anonym = new Runnable() {
public void run() {
}
};
}
}

In order to get the name of inner class write down the following:

anonym.getClass().toString().

You’ll get something like that: Anonymous$1.
Anonymous class can be either static or non-static. It depends on the block in which the class have been declared. In the previous example the anonymous class was static. In this case we can create the second instance of this class in such a way:

Runnable anonym2 = (Runnable) anonym
.getClass().newInstance().

There is no need in type cast in JDK 1.5.
If the anonymous class was declared in non-static block, we have to provide a reference to the outer class to the proper constructor (in reflection veritas!). In the other case we’ll get the InstantiationException.
Here we have an example (determining of proper constructor and exception handling are not shown below):

public class Anonymous {
public void nonStaticMethod() {
Runnable anonym = new Runnable() {
public void run() {
}
};
Constructor[] constructors = anonym.getClass()
.getDeclaredConstructors();
Object[] params = new Object[1];
params[0] = this;

Runnable anonym2 = (Runnable) constructors[0]
.newInstance(params);
}

public static void main(String[] args) {
Anonymous example = new Anonymous();
example.nonStaticMethod();
}
}

In this example we have to use getDeclaredConstructors instead of getConstructors. Method getConstructors will return only public constructors, while needed constructor is protected one.


Have a nice day.


Technorati Tags: , ,

6 Comments:

Ricky Clarkson said...

If an anonymous class can only be instantiated once, why does this appear to work fine?

for (int a=0;a<1000;a++) new Object(){};

Ganeshji Marwaha said...

very interesting information. Thanks for sharing.

Scott Vachalek said...

I agree with points 2 and 3 (although Ricky's example without reflection is even stronger) but I'd advise against anyone thinking of "Foo$1" as a "name". The compiler needs to assign it a name but different compilers will choose differently. Also, a small, unrelated change in the source code could change the name even on the same compiler.

Peter Lawrey said...

An anonymous class does need to be declared static because it is a static class if it defined in a static context.

e.g.

public static Runnable newRunnable() {
return new Runnable() {
public void run() {
System.out.println("Hello world");
}
}
}

This anonymous class is the same as defining a static nested class.

public static Runnable newRunnable() {
return new MyRunnable();
}

static class MyRunnable implements Runnable {
public void run() {
System.out.println("Hello world");
}
}
}

Peter Lawrey said...

Another example is the following which prints

Main$1()

public class Main {
public static void main(String... args) throws IllegalAccessException, InstantiationException {
Runnable run = new Runnable() {
public void run() {
}
};
for(Constructor cons: run.getClass().getDeclaredConstructors())
System.out.println(cons);
Runnable run2 = run.getClass().newInstance();
}
}

Marco Rietveld said...

And another example to prove that anonymous inner static classes DO exist:

class test_inner_static {
static int staticYes = 0;
final int notStaticThenNo = 2;

static Object o = new Object() {
public void func() {
int goed = staticYes + 1;
int fout = notStaticThenNo + 2;
}
};
}

The error message says that notStaticthenNo can't be referred (to) from a non-static context..