2016-03-12 3 views
21

Welcher Zugriffsmodifizierer muss ich in einer abstrakten Klasse für eine Methode verwenden, , damit die Unterklassen entscheiden können, ob sie öffentlich sein soll oder nicht? Ist es möglich, einen Modifizierer in Java zu "überschreiben" oder nicht?Kann eine überschreibende Methode einen anderen Zugriffsbezeichner als den in der Basisklasse haben?

public abstract class A { 

    ??? void method(); 
} 

public class B extends A { 
    @Override 
    public void method(){ 
     // TODO 
    } 
} 

public class C extends B { 
    @Override 
    private void method(){ 
     // TODO 
    } 
} 

Ich weiß, dass es ein Problem mit einer statischen Bindung, wenn jemand ruft:

// Will work 
A foo = new B() 
foo.method(); 

// Compiler ? 
A foo = new C(); 
foo.method(); 

Aber vielleicht gibt es eine andere Art und Weise. Wie kann ich das erreichen?

+0

Sie können das überschriebene Finale aber nicht privat machen. Sie können es auch veraltet machen und eine nicht unterstützte Operation Exception werfen (Guava tut das für unveränderbare Sammlungen) –

Antwort

12

Es ist möglich, die Einschränkung zu entspannen, aber nicht, um es restriktiver:

public abstract class A { 
    protected void method(); 
} 

public class B extends A { 
    @Override 
    public void method(){ // OK 
    } 
} 

public class C extends A { 
    @Override 
    private void method(){ // not allowed 
    } 
} 

die ursprüngliche Methode zu machen private funktioniert auch nicht, da eine solche Methode in Unterklassen nicht sichtbar ist und daher nicht überschrieben werden kann.

würde ich empfehlen, interface s unter Verwendung des Verfahrens, um selektiv aussetzen oder ausblenden:

public interface WithMethod { 
    // other methods 
    void method(); 
} 

public interface WithoutMethod { 
    // other methods 
    // no 'method()' 
} 

public abstract class A { 
    protected void method(); 
} 

public class B extends A implements WithMethod { 
    @Override 
    public void method(){ 
     //TODO 
    } 
} 

public class C extends B implements WithoutMethod { 
    // no 'method()' 
} 

... dann nur mit den Instanzen über die Schnittstellen arbeiten.

+0

soll ich dann nicht nur die Schnittstellen benutzen und die abstrakte Klasse fallen lassen? – jam

+0

Sie können, wenn Sie es nicht für die Wiederverwendung von Code benötigen. –

+1

In Ihrem Beispielcode würde eine Instanz der Klasse 'C' noch * eine' Methode() 'aus der Klasse' B' haben? Seit 'C erweitert B'. Das Hinzufügen von 'implements WithoutMethod' für die Klasse' C' kann *** 'method()' nicht aus der Klasse 'C' * entfernen. Recht? Also "* funktioniert nur mit den Instanzen über die Schnittstellen. *" Nur lassen Sie ** ** vortäuschen, dass 'C' keine' Methode() 'hat? – Tersosauros

3

Hier ist ein Teil des @Override Vertrags.

Die Antwort ist: es gibt keine Möglichkeit zu erreichen, was Sie haben.

The access level cannot be more restrictive than the overridden method's access level. For example: if the superclass method is declared public then the overridding method in the sub class cannot be either private or protected.

Dies ist kein Problem in Bezug auf abstract Klassen nur, sondern alle Klassen und Methoden.

8

Beim Überschreiben von Methoden können Sie den Modifizierer nur in einen breiteren ändern, nicht umgekehrt. Zum Beispiel würde dieser Code gültig sein:

public abstract class A { 

    protected void method(); 
} 

public class B extends A { 
    @Override 
    public void method() { } 
} 

Wenn Sie jedoch die Sichtbarkeit zu verengen versuchen, Sie einen Fehler bei der Kompilierung bekommen würden:

public abstract class A { 
    protected void method(); 
} 

public class B extends A { 
    @Override 
    private void method() {} 
} 

Für Ihren Fall würde ich vorschlagen C nicht Umsetzung A zu machen, als A ‚s Abstraktion impliziert, dass es eine nicht-privat ist method():

public class C { 
    private void method(){ 
     //TODO 
    } 
} 

Eine weitere Option ist die method() Implementierung in C warf eine Runtime zu machen:

public class C extends A { 

    @Override 
    public void method(){ 
     throw new UnsupportedOperationException("C doesn't support callbacks to method()"); 
    } 
} 
+1

wenn ich Methode in A geschützter und in C schützte, würde es funktionieren und Zugang von außerhalb des Pakets oder der Unterklasse wäre nicht möglich ? – jam

+0

Ja, aber er will es privat. –

3

Theorie:

Sie haben die ermittelten Modifikatoren bestellen:

public <- protected <- default-access X<- private 

Wenn Sie die Methode überschreiben, können Sie erhöhen, aber nicht den Modifikator Niveau verringern.Zum Beispiel

public -> [] 
protected -> [public] 
default-access -> [public, default-access] 
private -> [] 

PRAXIS:

In Ihrem Fall können Sie nicht ??? in einigen Modifikator drehen, weil privatedie niedrigsten Modifikator und private Klassen Mitglieder werden nicht vererbt.

+4

endgültig übergehen. Das ist falsch, obwohl es ein weit verbreiteter Mythos ist. In der Tat ist die richtige Reihenfolge "öffentlich -> geschützt -> Standardzugriff -> privat". Sie können auf geschützte Member aus anderen Klassen im selben Paket * und * aus Unterklassen in anderen Paketen zugreifen, während der Standardzugriff nur das vorherige bereitstellt. Siehe [JLS 8.4.8.3] (https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.8.3). –

+0

@SergeyTachenov, Sie haben Recht, ich korrigierte – Andrew

6

Was Sie verlangen, ist aus sehr guten Gründen nicht möglich.

Die Liskov substitution principle sagt im Grunde: eine Klasse S ist eine Unterklasse einer anderen Klasse T nur dann, wenn Sie jedes Auftreten von einigen "T-Objekt" mit einigen "S-Objekt" ersetzen können - ohne zu bemerken.

Wenn Sie zulassen würden, dass S eine öffentliche Methode auf private reduziert, können Sie das nicht mehr tun. Denn plötzlich ist diese Methode, die man mit ein paar T ... anrufen könnte, nicht mehr verfügbar.

Lange Rede kurzer Sinn: Vererbung ist nichts, was einfach vom Himmel fällt . Es ist eine Eigenschaft von Klassen, die Sie als Programmierer verantwortlich sind. Mit anderen Worten: Vererbung bedeutet mehr als nur "Klasse S erweitert T" in Ihren Quelltext zu schreiben!

+0

Der LSP erfordert, dass Basisklassenmethoden existieren und eine definierte Funktion in allen abgeleiteten Klassen haben. Es ist nicht erforderlich, dass die Funktion in allen abgeleiteten Klassen tatsächlich * nützlich * ist. Beispielsweise wäre es legitim, dass der Vertrag einer abgeleiteten Klasse angibt, dass eine virtuelle Basisklasseneigenschaft für alle Instanzen dieser abgeleiteten Klasse immer "false" zurückgibt. In solchen Fällen kann es durchaus sinnvoll sein, dieses Member in der abgeleiteten Klasse zu verbergen, da es keinen Grund gibt, es in einer Referenz * bekannt * aufzurufen, um ein abgeleitetes Klassenobjekt zu identifizieren. – supercat

+0

Sie haben es selbst gesagt: Die Methode muss in der abgeleiteten Klasse existieren. Es privat zu machen ist das Gleiche, als wenn man es komplett aus der Außenperspektive entfernt. Und natürlich kannst du mit einem "S" machen, was S erlaubt; aber das ist der springende Punkt des "transparenten Austauschs"; Sie greifen auf das S als T zu. Ich bin mir also nicht sicher, was Sie sagen wollen. – GhostCat

+0

Wenn Empfänger eines Verweises auf eine Instanz einer Klasse die Methode "Wizzle()" verwenden möchten, wenn sie eine solche hat, oder eine andere Sequenz von Methoden ausführen, die den gleichen Effekt, jedoch weniger effizient, erzielen, und einige Ableitungen "Wizzle()" und andere nicht, kann es hilfreich sein, wenn die Basisklasse eine "CanWizzle()" - Eigenschaft und eine "Wizzle()" -Methode enthält, mit dem Vertrag, wenn "CanWizzle()" zurückgibt true Die Methode "Wizzle()" gibt etwas Nützliches zurück, und wenn "CanWizzle()" false zurückgibt, ist dies möglicherweise nicht der Fall. Eine abgeleitete Klasse, die "Wizzle()" nicht kann ... – supercat

4

Dies ist wegen der Polymorphie unmöglich. Folgendes berücksichtigen. Sie haben die Methode in der Klasse A mit einem Zugriffsmodifikator, der nicht private ist. Warum nicht privat? Denn wenn es privat war, dann konnte keine andere Klasse von ihrer Existenz wissen. Also muss es etwas anderes sein, und etwas anderes muss von irgendwo zugänglich sein.

Angenommen, Sie übergeben eine Instanz der Klasse C an irgendwo. Aber Sie upCast es A vorher, und so am Ende Sie diesen Code mit irgendwo:

void somewhereMethod(A instance) { 
    instance.method(); // Ouch! Calling a private method on class C. 
} 

Ein schönes Beispiel, wie diese gebrochen wurde QSaveFile in Qt ist. Anders als Java erlaubt C++ tatsächlich, Zugriffsrechte zu reduzieren. Also taten sie genau das und verbieten die close() Methode. Was sie damit endete ist eine QIODevice Unterklasse, die nicht wirklich eine QIODevice mehr ist. Wenn Sie einen Zeiger auf QSaveFile an eine Methode übergeben, die QIODevice* akzeptiert, können sie weiterhin close() aufrufen, da es in QIODevice öffentlich ist. Sie "fixierten" dies, indem sie QSaveFile::close() (was privat ist) anrufen abort(), wenn Sie so etwas tun, stürzt Ihr Programm sofort ab. Keine sehr schöne "Lösung", aber es gibt keine bessere. Und es ist nur ein Beispiel für schlechtes OO-Design. Deshalb lässt Java das nicht zu.

bearbeiten

Nicht, dass ich verpasst, dass Ihre Klasse abstrakt ist, aber ich vermisste auch die Tatsache, dass B extends C, nicht A. Auf diese Weise ist das, was Sie tun möchten, völlig unmöglich. Wenn die Methode in B public ist, wird sie auch in allen Unterklassen öffentlich sein. Das einzige, was Sie tun können, ist zu dokumentieren, dass es nicht und genannt werden sollte, um es vielleicht zu überschreiben, um UnsupportedOperationException zu werfen. Aber das würde zu den gleichen Problemen führen wie bei QSaveFile.Denken Sie daran, dass Benutzer Ihrer Klasse möglicherweise nicht einmal wissen, dass es sich um eine Instanz von C handelt, damit sie nicht einmal die Möglichkeit haben, ihre Dokumentation zu lesen.

Insgesamt ist es nur eine sehr schlechte Idee OO-weise. Vielleicht sollten Sie eine andere Frage zu dem genauen Problem stellen, das Sie mit dieser Hierarchie lösen wollen, und vielleicht erhalten Sie einige anständige Ratschläge, wie Sie es richtig machen können.

Verwandte Themen