A Curious Java Language Feature and How it Produced a Subtle Bug

Java’s visibility rules are tricky at times. Do you know what this will print?

package p;

import static p.A.x;

class A {
    static String x = "A.x";
}

class B {
    String x = "B.x";
}

class C {
    String x = "C.x";

    class D extends B {
        void m() {
            System.out.println(x);
        }
    }
}

public class X {
    public static void main(String[] args) {
        new C().new D().m();
    }
}

It will print (highlight to see the solution):

B.x

Because:

The super type B's members hide the enclosing type C's members, 
which again hide the static import from A.

How can this lead to bugs?

The problem isn’t that the above code is tricky per se. When you write this logic, everything will work as expected. But what happens if you change things? For instance, if you mark the super type’s attribute as private:

package p;

import static p.A.x;

class A {
    static String x = "A.x";
}

class B {
    private String x = "B.x"; // Change here
}

class C {
    String x = "C.x";

    class D extends B {
        void m() {
            System.out.println(x);
        }
    }
}

public class X {
    public static void main(String[] args) {
        new C().new D().m();
    }
}

Now, suddenly, B.x is no longer visible from within method m(), so the rules are now:

Enclosing member hides static import

And we get the result of

C.x

Of course, we can change this again to the following code:

package p;

import static p.A.x;

class A {
    static String x = "A.x";
}

class B {
    private String x = "B.x";
}

class C {
    String xOld = "C.x"; // Change here

    class D extends B {
        void m() {
            System.out.println(x);
        }
    }
}

public class X {
    public static void main(String[] args) {
        new C().new D().m();
    }
}

As we all know, 50% of variables that are no longer needed are renamed to “old”.

Now, in this final version, there’s only one possible meaning of x inside of m(), and that’s the statically imported A.x, thus the output is:

A.x

Subtleties across a larger code base

These refactorings have shown that making something less visible is dangerous because a sub type might have depended on it, but for some freak coincidence, there was another member in a less “important” scope by the same name that now jumps in and keeps you from getting a compiler error.

The same can happen if you make something that was private more visible. Suddenly, it might be in scope on all its subtypes, even if you didn’t want it to be in scope.

Likewise, with the static import, we could run into a similar issue. When your code depends on a static import, it might suddenly be hidden by some member of the same name, e.g. in a super type. The author of the change might not even notice, because they’re not looking at your subtype.

Conclusion

The conclusion is, once more, not to rely on subtyping too much. If you can make your classes final, no one will ever override them and accidentally “profit” from your newly added members.

Besides that, every time you do a visibility change of your members, be very careful about potential members that are named the same accidentally.

18 thoughts on “A Curious Java Language Feature and How it Produced a Subtle Bug

  1. It sound strange that the compiler doesn’t complain about trying to override member “x” of class B from subclass C.

  2. It seems like you did a number of things very unlike conventional practice to get this to happen. I didn’t even know you could put multiple top-level classes into the same .java file. I certainly wouldn’t have named both static and non-static and outer and inner class members with the same name.

    • Actually I realize that the in-the-same-file thing isn’t really relevant (whether it’s actually legal or poetic license for the sake of brevity). But I’ve been programming in Java for over 15 years and I don’t think I’ve ever encountered a bug due to this sort of thing.

    • AHA! The concept you’re looking for is compilation unit (.java file) vs. top level type (classes inside a .java file), out of which only one may be public, and if it is, it must match the .java file name:
      https://docs.oracle.com/javase/specs/jls/se8/html/jls-7.html#jls-TypeDeclaration

      Don’t say you wouldn’t have those members. Sure, “x” seems like a far-fetched name. But what about something like “name”? Could happen. By accident if you overuse inheritance…

      • Interesting. (I would have used the term “module” myself, but I said “.java file” to avoid chance of miscommunication.) Is there a particular use that this multiple-top-level-type-per-compilation-unit comes in handy for?

        Well, I don’t overuse inheritance. ;^) I also don’t ever import static members that aren’t really constants (i.e. static final String X = “A.x”;).

        • I’m not sure about the utility of the multiple-top-level-type-per-compilation-unit feature. I mean, all those types are required to be package-private, sure, but then we could still put them in a separate .java file of the package. It would make sense if the types were “somewhat private” as in visible only to the compilation unit, but that sort of visibility doesn’t exist in the JVM, and neither does the whole notion of putting classes in a single compilation unit, because you cannot have several classes per “.class file”.

          I guess it’s simply a historic feature where you want to create a quick-and-dirty program, outsourcing some code to separate classes, without the hassle of creating all those files. That’s neat from a readability perspective if you think about it. Other languages built on top of the JVM allow it as well.

          • Well it’s good to know about for such quick and dirty cases, anyway.

            For that “somewhat private” use case you talk about, I often create private static inner classes. I’ve often wished java had just a little scope-control functionality. Something like the c++ friend declaration, or the ability to make a whole package only visible from within the classes of another specific package.

  3. This is certainly reminiscent of (and yet another way to) “shoot your self in the foot in programming language X”.

    Lukas has kindly included some of my previous commentary in his (linked article) on “make your classes final”… but I nonetheless protest (mildly) that solving THIS article’s problem by making classes ‘final’ is akin to avoiding stubbing one’s toe by amputating one’s foot. 🙂

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