Java-Sprachfunktion und wie sie einen subtilen Fehler verursachte

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-Sprachfunktion und wie sie einen subtilen Fehler verursachte
So komprimieren Sie eine JAVA-CSV-Datei und verwalten sie in einem Byte-Array
Schreiben Sie eine Klasse in Kotlin und nennen Sie sie in Java
So konvertieren Sie A in a und a in A mit logischem Produkt und Summe in Java
So entwickeln und registrieren Sie eine Sota-App in Java
Wie langsam ist Javas Scanner?
So erstellen Sie ein Java-Array
So testen Sie eine private Methode und verspotten sie teilweise in Java
So zeichnen Sie JFR (Java Flight Recorder) auf und geben eine Dump-Datei aus
Ich habe eine Lambda-Funktion in Java geschrieben und mit SAM bereitgestellt
So erstellen Sie eine App mit einem Plug-In-Mechanismus [C # und Java]
Was ist eine Klasse in der Java-Sprache (3 /?)
So erstellen Sie eine Java-Kalenderzusammenfassung
Ein Blick auf Jenkins, OpenJDK 8 und Java 11
[Einführung in Java] So schreiben Sie ein Java-Programm
[Java] Einen Fehler im JDK-Compiler beheben
[Java] Wie man Dateien ausgibt und schreibt!
Was ist eine Klasse in der Java-Sprache (1 /?)
Wie erstelle ich einen Discord Bot (Java)
Was ist eine Klasse in der Java-Sprache (2 /?)
Ein Java-Ingenieur verglich Swift, Kotlin und Java.
Grundlagen der Java-Entwicklung ~ So schreiben Sie ein Programm (Ablauf und bedingte Verzweigung) ~
So konvertieren Sie einen Wert eines anderen Typs und weisen ihn einer anderen Variablen zu