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?

python


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:

python


puts '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 subtype 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 supertype. The author of the change might not even notice, because they’re not looking at your subtype.

Final thoughts:

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.

Related blog:

Spring boot interiview questions

Recommended Posts

Java Language Feature and How it Produced a Subtle Bug
How to ZIP a JAVA CSV file and manage it in a Byte array
Write a class in Kotlin and call it in Java
How to convert A to a and a to A using AND and OR in Java
How to develop and register a Sota app in Java
How slow is a Java Scanner?
[Java] How to create a folder
How to make a Java array
How to test a private method in Java and partially mock that method
How to record JFR (Java Flight Recorder) and output a dump file
I wrote a Lambda function in Java and deployed it with SAM
How to make an app with a plugin mechanism [C # and Java]
What is a class in Java language (3 /?)
How to make a Java calendar Summary
A look at Jenkins, OpenJDK 8 and Java 11
[Introduction to Java] How to write a Java program
[Java] Stepping on a JDK compiler bug
[Java] How to output and write files!
What is a class in Java language (1 /?)
How to make a Discord bot (Java)
What is a class in Java language (2 /?)
A Java engineer compared Swift, Kotlin, and Java.
How to print a Java Word document
Basics of Java development ~ How to write a program (flow and conditional branching) ~
How to convert a value of a different type and assign it to another variable