Java: Zugriffsmodifikatoren bei überladenen Methoden

von Hubert Schmid vom 2013-08-25

Zugriffsmodifikatoren beschränken den Zugriff und Überladung ermöglicht statische Polymorphie. Diese beiden Konzepte sind nur scheinbar unabhängig voneinander. Tatsächlich ist die Überladung sehr eng an die Zugriffsbeschränkungen gekoppelt. Was das bedeutet zeige ich an der folgenden Klasse, die drei überladene Konstruktoren enthält.

public class Gadget { public Gadget(Object arg) { System.out.println("Object-" + arg); } protected Gadget(CharSequence arg) { System.out.println("CharSequence-" + arg); } private Gadget(String arg) { System.out.println("String-" + arg); } }

Welche Ausgabe erzeugt der Aufruf new Gadget("foobar")?

Die Antwort ist einfach: Das hängt davon ab. Es hängt davon ab, an welcher Stelle der Aufruf steht, wobei sich die folgenden Fälle unterscheiden lassen:

Wie man sieht berücksichtigt der Mechanismus für die Überladung die Zugriffsbeschränkungen. Im ersten Schritt werden nur Konstruktoren beziehungsweise Methoden mit passenden Zugriffsmodifikatoren in Betracht gezogen. Ist diese Menge nicht leer, dann wird im zweiten Schritt der Konstruktor beziehungsweise die Methode ausgewählt, deren Parametertypen am Besten zu den statischen Typen der Argumente passen.

Auf den ersten Blick erscheint dieses Vorgehen sinnvoll zur Unterstützung der Kapselung. Tatsächlich fällt es jedoch in die Kategorie Gut gemeint. Denn es durchbricht die ansonsten klaren Strukturen.

Die wesentliche Eigenschaft von Zugriffsmodifikatoren (access modifiers) ist, dass sie verwendet werden um den Zugriff zu beschränken. Die Änderung der Sichtbarkeit von Konstruktoren und Methoden ist zwar wünschenswert – doch leider gibt es dafür keine Mechanismen in Java. Lediglich die Überladung tanzt aus der Rolle, indem sie Zugriff und Sichtbarkeit miteinander vermischt, und Zugriffsmodifikatoren als Sichtbarkeitsmodifikatoren (visibility modifiers) um-interpretiert.

In der Praxis bedeutet dieser Bruch, dass die Programmiersprache unnötigerweise Fehler verdeckt – anstatt sie aufzudecken. Dabei sollte man berücksichtigen, dass der problematische Code nicht immer so offensichtlich wie im obigen Beispiel ist. Konstruktoren und Methoden können sich auf vielfältige und wenig intuitive Weise überladen, wie beispielsweise der Aufruf new Widget(System.currentTimeMillis()) mit nachfolgender Klasse.

public class Widget { public Widget(int arg) { } public Widget(Object... arg) { } }

Daher: Vorsicht bei überladenen Konstruktoren und Methoden mit unterschiedlichen Zugriffsmodifikatoren. Denn Java macht die Sache unnötig schwierig und fehleranfällig.