Explaining Java Inner Classes

The Problem

Today I had a colleague asking me to help her find a NotSerializableException. The code was like the following:

@ApplicationScoped
public class MyBusinessWindowHelper {
  ...
  public Button.ClickListener createExcelExportClickListener(Table t, String fileName) {
    return new Button.ClickListener() {
      @Override
      public void buttonClick(Button.ClickEvent clickEvent) {
        some action...
      }  
    }
  } 
}

The Button.ClickListener and Table are Serializable. The inner class also doesn’t store anything else which is not Serializable, so where is the problem?

People who found the answer in under 1 minute are probably under the top 1% Java programmers. If you don’t know the answer yet (not a shame, this is pretty hardcore stuff!) then read on.

The difference between a static and a non-static inner class

Static inner classes

Let’s first discuss static inner classes. They are quite the same like any other class which is lying around on your disk. In fact it makes almost no difference if you have the class located inside another class or as top level class. In terms of the generated bytecode there is really no difference.

Non-static inner classes

Non-static inner classes have access to the members of the outer class.

public class OuterClass
{
    private int meaningOfLife = 42;
    
    public class InnerClass {
        public int getMeaningOfLife() {
            return meaningOfLife;
        }
    }
}

As you can see the InnerClass can return the variable of the OuterClass. But how does that work? How does the inner class know about the outer class?

The answer is stunning but simple: You DON’T get what you see!

Let’s look at the bytecode which gets generated:

$> javac OuterClass.java
$> javap -c OuterClass\$InnerClass.class

will give us the following output:

Compiled from "OuterClass.java"
public class OuterClass$InnerClass {
  final OuterClass this$0;

  public OuterClass$InnerClass(OuterClass);
    Code:
       0: aload_0
       1: aload_1
       2: putfield      #1                  // Field this$0:LOuterClass;
       5: aload_0
       6: invokespecial #2                  // Method java/lang/Object."":()V
       9: return

  public int getMeaningOfLife();
    Code:
       0: aload_0
       1: getfield      #1                  // Field this$0:LOuterClass;
       4: invokestatic  #3                  // Method OuterClass.access$000:(LOuterClass;)I
       7: ireturn
}

There are 2 things which pop out:

final OuterClass this$0;

and

public OuterClass$InnerClass(OuterClass);

But hey, where does this come from? We did not have any constructor with an OuterClass parameter!

The answer is stunning but simple: Java did that for us!

Java automatically generates the this pointer of the outer class into any non-static inner class. And each constructor will also get an additional this parameter which is used to initialise this final field. Of course now it is possible for Java to use the generated outer class field (this$0) to access members of the outer class.

And what about anonymous classes?

Well, anonymous classes are just inner-classes without any specified name. Of course they will also get the this pointer as parameter.

And this is EXACTLY the reason why my colleague did get the NotSerializableException: Because storing the ClickListener in a View and serialising it away to another cluster also tried to serialise away the outer class (MyBusinessWindowHelper).

Btw, this is not only an issue if you play catch and hide with NotSerializableExceptions but also if you are hunting down mem-leaks!

A few more thoughts

That also explains why non-static inner classes cannot get proxied (because they do not have a default constructor). Plus it explains why they cannot be used as CDI or EJB beans.

All that is pretty obvious if one knows how the system works internally, isn’t?

Advertisements

About struberg
I'm an Apache Software Foundation member blogging about Java, µC, TheASF, OpenWebBeans, Maven, MyFaces, CODI, GIT, OpenJPA, TomEE, DeltaSpike, ...

3 Responses to Explaining Java Inner Classes

  1. I think the reason for the problem will be found by more than 1% of programmers. I did it immediately. I remember the synthetic constructor was discussed in the original ‘Java in a nutshell’ published about 15 years back.
    But I didn’t understand why they cannot be CDI beans.

  2. struberg says:

    Well, it’s much easier if you first read the topic of the whole blog post, but I really think it’s not known to that many programmers as it should. Most people didn’t read JiNS 15 years ago as you and a few other old Java hackers did 😉

    Regarding why they cannot be CDI beans. It’s basically that you cannot do a newInstance on them without having an instance of the outer class. So you cannot inject it into some arbitrary other class. It would also not be possible to give it any other scope than @Dependent because those classes are not proxyable (you need a default ct without parameter for that).

  3. Pingback: Java Weekly 46: Joda-Time to Java8, new Apache Tamaya, Java internals and more... - Thoughts on Java -

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: