|
2005-09-02
The link to the outer class
2002-12-27 The Java Specialists' Newsletter [Issue 062] - The link to the outer class Author: Dr. Heinz M. KabutzJDK version: Category: Language If you are reading this, and have not subscribed, please consider doing it now by going to our subscribe page. You can subscribe either via email or RSS. Welcome to the 62nd edition of The Java(tm) Specialists' Newsletter sent to 5450 Java Specialists in 91 countries. Here I am, sitting in my shorts on a beautiful starry night on our balcony, listening to the Guinea Fowls in our trees, trying to churn out yet another newsletter to appeal to your cerebral impulses. Guinea Fowls make a very strange sound, which is extremely annoying to some, and music to others. Fortunately for me (and the Guinea Fowls), I like their squawking. From the sunburnt-southern-hemispherer to all eggnog-drinking-northern-hemispherers: Relax, the days are getting longer again :-) Don't you just love those Out-Of-Office messages? Boy, am I going to get a lot of those with this newsletter... 2003 - Europe Design Patterns Tour - please look at my previous newsletter. The link to the outer class Consider the following classes: public abstract class Insect { public Insect() { System.out.println("Inside Insect() Constructor"); printDetails(); } public void printDetails() { System.out.println("Just an insect"); } } public class Beetle extends Insect { private final int legs; public Beetle(int legs) { System.out.println("Inside Beetle() Constructor"); this.legs = legs; } public void printDetails() { System.out.println("The beetle has " + legs + " legs"); if (legs < 6) { System.out.println("Ouch"); } } } public class BeetleTest { public static void main(String[] args) { Beetle sad_bug = new Beetle(5); // lost one leg in an // argument with his wife Beetle happy_bug = new Beetle(6); // the wife bug ;-) } } Stop for a moment and think of what the effect would be of running BeetleTest. Don't read further until you have decided what would happen. I hope you didn't peep :-) Here is the output: Inside Insect() Constructor The beetle has 0 legs Ouch Inside Beetle() Constructor Inside Insect() Constructor The beetle has 0 legs Ouch Inside Beetle() Constructor Yes, even though legs was final, we were able to access it before it was initialised. What is more, we are able to call the subclass' methods from the constructor of the superclass, before the subclass had been initialised! This should come as no surprise to you, since by being subscribed to The Java(tm) Specialists' Newsletter you would be classed as a Java Specialist. Yes? No ... ? But, the plot thickens. Look at the following class: public class NestedBug { private Integer wings = new Integer(2); public NestedBug() { new ComplexBug(); } private class ComplexBug extends Insect { public void printDetails() { System.out.println(wings); } } public static void main(String[] arguments) { new NestedBug(); } } When we run this code, we get a NullPointerException: Inside Insect() Constructor java.lang.NullPointerException at NestedBug.access$0(NestedBug.java:2) at NestedBug$ComplexBug.printDetails(NestedBug.java:8) at Insect.(Insect.java:4) at NestedBug$ComplexBug.(NestedBug.java:6) at NestedBug.(NestedBug.java:4) at NestedBug.main(NestedBug.java:12) Exception in thread "main" A friend of mine once had this problem, so my first thought was that somehow, because wings was null, we ended up with a NullPointerException when printing it. That explanation did not make sense, because calling toString() on a null pointer is supposed to just return null. My friend changed his code to the following: public class NestedBug2 { private Integer wings = new Integer(2); public NestedBug2() { new ComplexBug(); } private class ComplexBug extends Insect { public void printDetails() { if (wings != null) { // line 8 System.out.println(wings); } } } public static void main(String[] arguments) { new NestedBug2(); } } Sadly, this does not make the NullPointerException go away: Inside Insect() Constructor java.lang.NullPointerException at NestedBug2.access$0(NestedBug2.java:2) at NestedBug2$ComplexBug.printDetails(NestedBug2.java:8) at Insect.(Insect.java:4) at NestedBug2$ComplexBug.(NestedBug2.java:6) at NestedBug2.(NestedBug2.java:4) at NestedBug2.main(NestedBug2.java:14) Exception in thread "main" But wait! The line with the NullPointerException is the line that simply checks whether wings is null!? Can the mere act of checking whether wings is null itself cause a NullPointerException? In this case it appears that it can! Or is the actual mistake on line 2? What is this access$0 method? To understand this strange behaviour, we have to understand what the constructor of the inner class consists of. The easiest way of doing this is to use JAD and decompile the class file with the -noinner option: jad -noinner NestedBug2$ComplexBug.class class NestedBug2$ComplexBug extends Insect { NestedBug2$ComplexBug(NestedBug2 nestedbug2) { this$0 = nestedbug2; } public void printDetails() { if (NestedBug2.access$0(this$0) != null) System.out.println(NestedBug2.access$0(this$0)); } private final NestedBug2 this$0; /* synthetic field */ } We also need to look at NestedBug2.class: jad -noinner NestedBug2.class public class NestedBug2 { public NestedBug2() { wings = new Integer(2); new NestedBug2$ComplexBug(this); } public static void main(String arguments[]) { new NestedBug2(); } static Integer access$0(NestedBug2 nestedbug2) { return nestedbug2.wings; } private Integer wings; } Again, I must urge you to spend a few minutes thinking about this. Step through what would happen in your head or scribble on a piece of paper, but try to understand. When printDetails() is called by Insect>, the handle to outer class (this$0) has not yet been set in the ComplexBug class, so that class passes null to the access$0 method, which of course causes a NullPointerException when it tries to access nestedbug2.wings as we would expect. I bet that many developers have run into this problem, but being under typical time pressure would have just coded around it until they got it working, without taking the time to think about what is happening. This experience leads us to some questions: Is this$0 a new type of keyword you did not know about? What happens when your inner class already contains a variable this$0? Is calling a method from a constructor a good idea in the first place? When, if ever, are inner classes a good idea? I will leave you with these questions to answer yourself over the festive season. For those of you who are actually working, I hope this newsletter has cheered up your workday and that you will be motivated to seek new nuggets of information inside the fascinating Java language. All the best for the new year. May 2003 bring you new opportunities to learn and grow, not just in the material world :-) Heinz Copyright 2000-2005 Maximum Solutions, South AfricaReprint Rights. Copyright subsists in all the material included in this email, but you may freely share the entire email with anyone you feel may be interested, and you may reprint excerpts both online and offline provided that you acknowledge the source as follows: This material from The Java(tm) Specialists' Newsletter by Maximum Solutions (South Africa). Please contact Maximum Solutions for more information.
# posted by Ching @ 10:09 pm
|