Warum ist super.super.method (); in Java nicht erlaubt?


360

Ich las diese Frage und dachte, das wäre leicht zu lösen (nicht, dass es ohne nicht lösbar wäre), wenn man schreiben könnte:

@Override
public String toString() {
    return super.super.toString();
}

Ich bin mir nicht sicher, ob es in vielen Fällen nützlich ist, aber ich frage mich, warum es nicht nützlich ist und ob so etwas in anderen Sprachen existiert.

Was denkt ihr?

EDIT: Zur Verdeutlichung: Ja, ich weiß, das ist in Java unmöglich und ich vermisse es nicht wirklich. Dies ist nichts, was ich erwartet hatte und war überrascht, einen Compilerfehler zu bekommen. Ich hatte gerade die Idee und diskutiere sie gerne.


7
super.super.toString()Wenn Sie anrufen möchten, widerspricht dies Ihrer eigenen Entscheidung, wenn Sie eine Klasse erweitern und so alle (nicht einige) ihrer Funktionen akzeptieren .
DayaMoon

Antworten:


484

Es verletzt die Kapselung. Sie sollten das Verhalten der übergeordneten Klasse nicht umgehen können. Es ist sinnvoll, manchmal das Verhalten Ihrer eigenen Klasse zu umgehen (insbesondere innerhalb derselben Methode), nicht jedoch das Ihrer Eltern. Angenommen, wir haben eine Basis "Sammlung von Gegenständen", eine Unterklasse, die "eine Sammlung roter Gegenstände" darstellt, und eine Unterklasse derjenigen, die "eine Sammlung großer roter Gegenstände" darstellt. Es ist sinnvoll zu haben:

public class Items
{
    public void add(Item item) { ... }
}

public class RedItems extends Items
{
    @Override
    public void add(Item item)
    {
        if (!item.isRed())
        {
            throw new NotRedItemException();
        }
        super.add(item);
    }
}

public class BigRedItems extends RedItems
{
    @Override
    public void add(Item item)
    {
        if (!item.isBig())
        {
            throw new NotBigItemException();
        }
        super.add(item);
    }
}

Das ist in Ordnung - RedItems kann immer sicher sein, dass alle darin enthaltenen Elemente rot sind. Nehmen wir nun an wir waren fähig super.super.add () aufzurufen:

public class NaughtyItems extends RedItems
{
    @Override
    public void add(Item item)
    {
        // I don't care if it's red or not. Take that, RedItems!
        super.super.add(item);
    }
}

Jetzt können wir hinzufügen, was wir wollen, und die Invariante RedItemsist gebrochen.

Ist das sinnvoll?


38
schönes Beispiel. Aber ich dachte, es ist ein schlechtes Design, wenn die Basisklasse Elemente akzeptiert, aber die abgeleitete Klasse lehnt sie ab, da die abgeleitete Klasse nicht als Ersatz für die Basisklasse verwendet werden kann (Verstoß gegen das Substitutionsprinzip). Ist das richtig oder ist eine solche Hierarchie sauber?
Johannes Schaub - Litb

5
Meinen Sie Komposition über Vererbung? Dieses Beispiel ist kein Grund, Vererbung zu vermeiden - obwohl es viele andere gibt.
Jon Skeet

3
@ Tim: Dies war nur ein leicht verständliches Beispiel für die Verletzung der Kapselung. Es könnte stattdessen eine Eigenschaft festlegen. Außerdem sind nicht alle Aspekte auf Typebene sichtbar. Generika sind nicht die Antwort auf alles.
Jon Skeet

12
@ JohannesSchaub-litb Ich denke, dass es gegen das Liskov-Substitutionsprinzip verstößt, denn wenn man den Vertrag von Item codiert und eine Instanz von RedItems verwendet, erhält man eine unerwartete NotRedItemException. Mir wurde immer beigebracht, dass eine Unterklasse eine Supermenge von Eingaben nehmen und eine Teilmenge von Ausgaben zurückgeben sollte. Mit anderen Worten, eine Unterklasse sollte niemals eine Eingabe ablehnen, die für die Superklasse gültig ist, oder eine Ausgabe erzeugen, die die Superklasse nicht erzeugen kann. Dies schließt das Auslösen eines Fehlers ein, den die Superklasse nicht auslöst.
Konstantin Tarashchanskiy

4
@piechuckerr: Manchmal Bücher, manchmal Blog-Beiträge, manchmal nur Erfahrung ...
Jon Skeet

72

Ich denke, Jon Skeet hat die richtige Antwort. Ich möchte nur hinzufügen, dass Sie durch Casting auf schattierte Variablen aus Superklassen von Superklassen zugreifen könnenthis :

interface I { int x = 0; }
class T1 implements I { int x = 1; }
class T2 extends T1 { int x = 2; }
class T3 extends T2 {
        int x = 3;
        void test() {
                System.out.println("x=\t\t"          + x);
                System.out.println("super.x=\t\t"    + super.x);
                System.out.println("((T2)this).x=\t" + ((T2)this).x);
                System.out.println("((T1)this).x=\t" + ((T1)this).x);
                System.out.println("((I)this).x=\t"  + ((I)this).x);
        }
}

class Test {
        public static void main(String[] args) {
                new T3().test();
        }
}

welches die Ausgabe erzeugt:

x = 3
super.x = 2
((T2) dies) .x = 2
((T1) dies) .x = 1
((I) dies) .x = 0

(Beispiel aus dem JLS )

Dies funktioniert jedoch nicht für Methodenaufrufe, da Methodenaufrufe basierend auf dem Laufzeittyp des Objekts bestimmt werden.


6
Wenn Sie eine Variable mit demselben Namen wie eine in einer Oberklasse haben und sie aus irgendeinem Grund nicht ändern können (oder dürfen), können Sie trotzdem auf die Variable der Oberklasse mit demselben Namen zugreifen. Was nützt es, fragst du? Nun ... ich habe es nie benutzt.
Michael Myers

Toller Link zum Ausdrucksdokument. Einige schöne Beispiele für Leute, die für das OCPJP studieren.
Gordon

Genial. Ich hätte nie daran gedacht, eine Besetzung zu verwenden.
Thomas Eding

Wie kommt es, dass die Klasse T1 die Variable x überschreibt? sein statisches Finale standardmäßig richtig?
Amarnath Harish

@amarnathharish: Es wird nicht überschrieben und Felder sind standardmäßig paketgeschützt und nicht endgültig.
Michael Myers

41

Ich denke, der folgende Code erlaubt in den meisten Fällen die Verwendung von super.super ... super.method (). (auch wenn es hässlich ist, das zu tun)

Zusamenfassend

  1. Erstellen Sie eine temporäre Instanz des Ahnen-Typs
  2. Kopieren Sie die Werte der Felder vom ursprünglichen Objekt in das temporäre
  3. Rufen Sie die Zielmethode für ein temporäres Objekt auf
  4. Kopieren Sie geänderte Werte zurück in das ursprüngliche Objekt

Verwendungszweck :

public class A {
   public void doThat() { ... }
}

public class B extends A {
   public void doThat() { /* don't call super.doThat() */ }
}

public class C extends B {
   public void doThat() {
      Magic.exec(A.class, this, "doThat");
   }
}


public class Magic {
    public static <Type, ChieldType extends Type> void exec(Class<Type> oneSuperType, ChieldType instance,
            String methodOfParentToExec) {
        try {
            Type type = oneSuperType.newInstance();
            shareVars(oneSuperType, instance, type);
            oneSuperType.getMethod(methodOfParentToExec).invoke(type);
            shareVars(oneSuperType, type, instance);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
    private static <Type, SourceType extends Type, TargetType extends Type> void shareVars(Class<Type> clazz,
            SourceType source, TargetType target) throws IllegalArgumentException, IllegalAccessException {
        Class<?> loop = clazz;
        do {
            for (Field f : loop.getDeclaredFields()) {
                if (!f.isAccessible()) {
                    f.setAccessible(true);
                }
                f.set(target, f.get(source));
            }
            loop = loop.getSuperclass();
        } while (loop != Object.class);
    }
}

10
Mit Reflexion kannst du alles machen, ja :) Du kannst Strings sogar veränderlich machen.
BalusC

23
Was für eine schreckliche Sache! Ich gebe dir +1, nur um herauszufinden, wie es überhaupt geht :)
Larry Watanabe

6
Es ist ein netter Trick, aber selbst das ist nicht immer gleichbedeutend mit dem Aufruf des nicht verfügbaren, aber benötigten) super.super, und das liegt daran, dass der super.super-Aufruf den Kontext von C (C + B + A) enthält, während Ihre Antworten eine Instanz erstellen von A ohne den Kontext von B und C. Diese Antwort funktioniert also nicht, wenn jedes doThat mit dem Namen getContext () und getContext in jeder Klasse unterschiedlich implementiert wurde. In Ihrer Antwort würde getContext () von A verwendet, während das Aufrufen der nicht verfügbaren Datei super.super zur Verwendung von getContext von C geführt hätte.
Inor

Hmm. In einigen Fällen könnten Sie möglicherweise den Einwand von inor mit dynamischen Proxys ( javahowto.blogspot.co.uk/2011/12/… ) überwinden und Methodenaufrufe an das ursprüngliche Objekt umleiten (Variablen nach jedem Aufruf synchronisieren)? Es scheint jedoch, dass Proxys alles erfordern, um eine Schnittstelle zu implementieren. Ich frage mich auch, ob es der Super-Super-Klasse möglich ist, eine ihrer Super-Super-Methoden aufzurufen, und Sie müssten diese NICHT umleiten ...
Erhannis

Ich sagte in einem anderen Kommentar, dass das Blockieren super.super.Programmierer dazu einlädt, neue, verschlungene und ungeheure Wege zu finden, um sich bei der Suche nach einer Problemumgehung in den Fuß zu schießen. Dies ist ein perfektes Beispiel dafür, weil Ihre Kollegen Sie wahrscheinlich so sehr dafür hassen werden, etwas zu schreiben so, dass sie dich persönlich und buchstäblich in den Fuß schießen. +1
Braden Best

11

Ich habe nicht genug Ruf, um Kommentare abzugeben, daher werde ich dies zu den anderen Antworten hinzufügen.

Jon Skeet antwortet ausgezeichnet mit einem schönen Beispiel. Matt B hat einen Punkt: Nicht alle Superklassen haben Supers. Ihr Code würde kaputt gehen, wenn Sie ein Super eines Super nennen würden, das kein Super hat.

Bei der objektorientierten Programmierung (was Java ist) dreht sich alles um Objekte, nicht um Funktionen. Wenn Sie eine aufgabenorientierte Programmierung wünschen, wählen Sie C ++ oder etwas anderes. Wenn Ihr Objekt nicht in die Superklasse passt, müssen Sie es der "Großelternklasse" hinzufügen, eine neue Klasse erstellen oder eine andere Superklasse finden, in die es passt.

Persönlich habe ich festgestellt, dass diese Einschränkung eine der größten Stärken von Java ist. Code ist im Vergleich zu anderen Sprachen, die ich verwendet habe, etwas starr, aber ich weiß immer, was mich erwartet. Dies hilft beim "einfachen und vertrauten" Ziel von Java. In meinen Augen ist es nicht einfach oder vertraut, super.super anzurufen. Vielleicht ging es den Entwicklern genauso?


3
Sie sagen "nicht alle Superklassen haben Supers". Nun, alle außer java.lang.Object, was Ihnen "null" geben könnte. Also ich würde sagen, fast alle haben Supers.
Tim Büthe

Jede Klasse, die der Anwendungsprogrammierer schreibt, hat ein "Super" (java.lang.Object nicht, aber der Anwendungsprogrammierer schreibt das nicht.)
finnw

2
Einfach zu lösen, indem super.super.super ... super zu einem Kompilierungsfehler wird, wenn zu viele Supers vorhanden sind. Wenn man bedenkt, dass Java nur eine öffentliche Vererbung hat, ändern Sie die Schnittstelle, wenn jemand die Vererbungshierarchie geändert hat. Ich würde mir also keine Sorgen machen, dass Super ^ n beängstigend ist.
Thomas Eding

7

Dafür gibt es einige gute Gründe. Möglicherweise haben Sie eine Unterklasse mit einer Methode, die falsch implementiert ist, aber die übergeordnete Methode ist korrekt implementiert. Da es zu einer Bibliothek eines Drittanbieters gehört, können / möchten Sie die Quelle möglicherweise nicht ändern. In diesem Fall möchten Sie eine Unterklasse erstellen, aber eine Methode überschreiben, um die super.super-Methode aufzurufen.

Wie einige andere Poster zeigen, ist es möglich, dies durch Reflexion zu tun, aber es sollte möglich sein, so etwas zu tun

(SuperSuperClass this) .theMethod ();

Ich beschäftige mich gerade mit diesem Problem - die schnelle Lösung besteht darin, die Superklassenmethode zu kopieren und in die Unterklassenmethode einzufügen :)


1
Das Casting vor dem Aufrufen einer Methode ändert nichts an der Methode, an die delegiert wird. Es wird immer die Implementierung der Unterklasse verwendet, niemals die der Superklasse. Siehe z. B. stackoverflow.com/questions/1677993/…
Joshua Goldberg,

1
@ Larry Dies ist genau die Situation, in der ich mich befand, und genau das Update, das ich verwendet habe. Guter Anruf!
bcr

Wenn Sie den Code der benötigten Methode haben, können Sie alle benötigten Felder öffnen und diesen Code in Ihrer Unterklasse ausführen.
Enyby

6

Zusätzlich zu den sehr guten Punkten, die andere gemacht haben, gibt es meines Erachtens noch einen weiteren Grund: Was ist, wenn die Oberklasse keine Oberklasse hat?

Da sich jede Klasse natürlich (zumindest) erweitert Object, super.whatever()wird immer auf eine Methode in der Oberklasse verwiesen. Aber was ist, wenn sich Ihre Klasse nur erweitert Object- worauf würde super.supersich das dann beziehen? Wie soll mit diesem Verhalten umgegangen werden - ein Compilerfehler, ein NullPointer usw.?

Ich denke, der Hauptgrund, warum dies nicht erlaubt ist, ist, dass es die Kapselung verletzt, aber dies könnte auch ein kleiner Grund sein.


4
Offensichtlich wäre es ein Compilerfehler - und es sind Informationen, die der Compiler hat.
Michael Borgwardt

4

Ich denke, wenn Sie eine Methode überschreiben und die gesamte Super-Class-Version davon (wie zum Beispiel für equals) verwenden möchten, möchten Sie praktisch immer zuerst die direkte Superclass-Version aufrufen, die wiederum die Superclass-Version aufruft, wenn dies gewünscht wird .

Ich denke, es macht nur selten Sinn (wenn überhaupt. Ich kann mir keinen Fall vorstellen, in dem dies der Fall ist), eine beliebige Superklassenversion einer Methode aufzurufen. Ich weiß nicht, ob das in Java überhaupt möglich ist. Dies kann in C ++ erfolgen:

this->ReallyTheBase::foo();

6
Es ist sinnvoll, wenn jemand eine Unterklasse mit einer falsch implementierten Methode geschrieben hat, die Oberklassenmethode jedoch 90% der Arbeit erledigt. Dann möchten Sie eine Unterklasse erstellen, die Methode überschreiben, die die Oberklassen-Oberklassenmethode aufruft, und Ihre eigenen 10% hinzufügen.
Larry Watanabe

3

Vermutlich, weil es nicht so oft verwendet wird. Der einzige Grund, warum ich es sehen konnte, ist, wenn Ihr direkter Elternteil einige Funktionen überschrieben hat und Sie versuchen, sie wieder auf das Original zurückzusetzen.

Das scheint mir gegen die OO-Prinzipien zu verstoßen, da der direkte Elternteil der Klasse enger mit Ihrer Klasse verwandt sein sollte als der Großelternteil.


3

Schauen Sie sich dieses Github-Projekt an, insbesondere die Variable objectHandle. Dieses Projekt zeigt, wie man die Großelternmethode eines Enkels tatsächlich und genau aufruft.

Für den Fall, dass der Link unterbrochen wird, ist hier der Code:

import lombok.val;
import org.junit.Assert;
import org.junit.Test;

import java.lang.invoke.*;

/*
Your scientists were so preoccupied with whether or not they could, they didn’t stop to think if they should.
Please don't actually do this... :P
*/
public class ImplLookupTest {
    private MethodHandles.Lookup getImplLookup() throws NoSuchFieldException, IllegalAccessException {
        val field = MethodHandles.Lookup.class.getDeclaredField("IMPL_LOOKUP");
        field.setAccessible(true);
        return (MethodHandles.Lookup) field.get(null);
    }

    @Test
    public void test() throws Throwable {
        val lookup = getImplLookup();
        val baseHandle = lookup.findSpecial(Base.class, "toString",
            MethodType.methodType(String.class),
            Sub.class);
        val objectHandle = lookup.findSpecial(Object.class, "toString",
            MethodType.methodType(String.class),
            // Must use Base.class here for this reference to call Object's toString
            Base.class);
        val sub = new Sub();
        Assert.assertEquals("Sub", sub.toString());
        Assert.assertEquals("Base", baseHandle.invoke(sub));
        Assert.assertEquals(toString(sub), objectHandle.invoke(sub));
    }

    private static String toString(Object o) {
        return o.getClass().getName() + "@" + Integer.toHexString(o.hashCode());
    }

    public class Sub extends Base {
        @Override
        public String toString() {
            return "Sub";
        }
    }

    public class Base {
        @Override
        public String toString() {
            return "Base";
        }
    }
}

Viel Spaß beim Codieren !!!!


Ihre Wissenschaftler waren so beschäftigt, ob sie konnten oder nicht, dass sie nicht aufhörten zu überlegen, ob sie sollten. Bitte tun Sie dies nicht wirklich ...: P 🤔
Tim Büthe

Ja, das sind die Worte des Kodierers, nicht meine. Meiner Meinung nach brauchen Sie es vielleicht eines Tages
Kyay

2

Wenn möglich, würde ich den Körper der super.super-Methode in eine andere Methode einfügen

class SuperSuperClass {
    public String toString() {
        return DescribeMe();
    }

    protected String DescribeMe() {
        return "I am super super";
    }
}

class SuperClass extends SuperSuperClass {
    public String toString() {
        return "I am super";
    }
}

class ChildClass extends SuperClass {
    public String toString() {
        return DescribeMe();
    }
}

Oder wenn Sie die Super-Super-Klasse nicht ändern können, können Sie Folgendes versuchen:

class SuperSuperClass {
    public String toString() {
        return "I am super super";
    }
}

class SuperClass extends SuperSuperClass {
    public String toString() {
        return DescribeMe(super.toString());
    }

    protected String DescribeMe(string fromSuper) {
        return "I am super";
    }
}

class ChildClass extends SuperClass {
    protected String DescribeMe(string fromSuper) {
        return fromSuper;
    }
}

In beiden Fällen ist die

new ChildClass().toString();

Ergebnisse zu "Ich bin super super"


Ich befand mich gerade in einer Situation, in der ich die SuperSuperClass und ChildClass besitze, aber nicht die SuperClass, und fand die erste Lösung nützlich.
Xofon


1
public class A {

     @Override
     public String toString() {
          return "A";
     }

}


public class B extends A {

     @Override
     public String toString() {
          return "B";
     }

}

public class C extends B {

     @Override
     public String toString() {
          return "C";
     }

}


public class D extends C {

     @Override
     public String toString() {
          String result = "";
          try {
                result = this.getClass().getSuperclass().getSuperclass().getSuperclass().newInstance().toString();
          } catch (InstantiationException ex) {
                Logger.getLogger(D.class.getName()).log(Level.SEVERE, null, ex);
          } catch (IllegalAccessException ex) {
                Logger.getLogger(D.class.getName()).log(Level.SEVERE, null, ex);
          }
          return result;
     }

}

public class Main {

     public static void main(String... args) {
          D d = new D();
          System.out.println(d);

     }
}

run: A BUILD SUCCESSFUL (Gesamtzeit: 0 Sekunden)


1
Ich verstehe, aber Sie erstellen eine neue Instanz, sodass dies nicht funktioniert, wenn das Objekt einen Status hat.
Tim Büthe

1

Das Aufrufen von super.super.method () ist sinnvoll, wenn Sie den Code der Basisklasse nicht ändern können. Dies passiert häufig, wenn Sie eine vorhandene Bibliothek erweitern.

Fragen Sie sich zuerst, warum Sie diese Klasse erweitern? Wenn die Antwort "weil ich sie nicht ändern kann" lautet, können Sie ein genaues Paket und eine Klasse in Ihrer Anwendung erstellen und eine ungezogene Methode neu schreiben oder einen Delegaten erstellen:

package com.company.application;

public class OneYouWantExtend extends OneThatContainsDesiredMethod {

    // one way is to rewrite method() to call super.method() only or 
    // to doStuff() and then call super.method()

    public void method() {
        if (isDoStuff()) {
            // do stuff
        }
        super.method();
    }

    protected abstract boolean isDoStuff();


    // second way is to define methodDelegate() that will call hidden super.method()

    public void methodDelegate() {
        super.method();
    }
    ...
}

public class OneThatContainsDesiredMethod {

    public void method() {...}
    ...
}

Beispielsweise können Sie org.springframework.test.context.junit4.SpringJUnit4ClassRunner erstellen in Ihrer Anwendung die Klasse sodass diese Klasse vor der realen Klasse aus jar geladen werden sollte. Schreiben Sie dann Methoden oder Konstruktoren neu.

Achtung: Dies ist ein absoluter Hack, und es wird dringend empfohlen, ihn zu verwenden, aber er funktioniert! Die Verwendung dieses Ansatzes ist aufgrund möglicher Probleme mit Klassenladern gefährlich. Dies kann auch jedes Mal zu Problemen führen, wenn Sie eine Bibliothek aktualisieren, die überschriebene Klassen enthält.


1

Ich hatte Situationen wie diese, in denen die Architektur gemeinsame Funktionen in einer gemeinsamen CustomBaseClass erstellen soll, die im Auftrag mehrerer abgeleiteter Klassen implementiert wird. Wir müssen jedoch die gemeinsame Logik für eine bestimmte Methode für eine bestimmte abgeleitete Klasse umgehen. In solchen Fällen müssen wir eine super.super.methodX-Implementierung verwenden.

Dies erreichen wir durch die Einführung eines booleschen Elements in der CustomBaseClass, mit dem die benutzerdefinierte Implementierung selektiv verschoben und bei Bedarf der Standard-Framework-Implementierung nachgegeben werden kann.

        ...
        FrameworkBaseClass (....) extends...
        {
           methodA(...){...}
           methodB(...){...}
        ...
           methodX(...)
        ...
           methodN(...){...}

        }
        /* CustomBaseClass overrides default framework functionality for benefit of several derived classes.*/
        CustomBaseClass(...) extends FrameworkBaseClass 
        {
        private boolean skipMethodX=false; 
        /* implement accessors isSkipMethodX() and setSkipMethodX(boolean)*/

           methodA(...){...}
           methodB(...){...}
        ...
           methodN(...){...}

           methodX(...){
                  if (isSkipMethodX()) {
                       setSKipMethodX(false);
                       super.methodX(...);
                       return;
                       }
                   ... //common method logic
            }
        }

        DerivedClass1(...) extends CustomBaseClass
        DerivedClass2(...) extends CustomBaseClass 
        ...
        DerivedClassN(...) extends CustomBaseClass...

        DerivedClassX(...) extends CustomBaseClass...
        {
           methodX(...){
                  super.setSKipMethodX(true);
                  super.methodX(...);
                       }
        }

Mit guten Architekturprinzipien, die sowohl im Framework als auch in der App befolgt werden, könnten wir solche Situationen jedoch leicht vermeiden, indem wir den hasA-Ansatz anstelle des isA-Ansatzes verwenden. Es ist jedoch nicht immer sehr praktisch, eine gut gestaltete Architektur zu erwarten, und daher die Notwendigkeit, sich von soliden Designprinzipien zu lösen und solche Hacks einzuführen. Nur meine 2 Cent ...


1

@ Jon Skeet Schöne Erklärung. IMO, wenn jemand die Methode super.super aufrufen möchte, muss man das Verhalten des unmittelbaren Elternteils ignorieren wollen, aber auf das Verhalten des Großelternteils zugreifen wollen. Dies kann durch Instanz von erreicht werden. Wie unten Code

public class A {
    protected void printClass() {
        System.out.println("In A Class");
    }
}

public class B extends A {

    @Override
    protected void printClass() {
        if (!(this instanceof C)) {
            System.out.println("In B Class");
        }
        super.printClass();
    }
}

public class C extends B {
    @Override
    protected void printClass() {
        System.out.println("In C Class");
        super.printClass();
    }
}

Hier ist Fahrerklasse,

public class Driver {
    public static void main(String[] args) {
        C c = new C();
        c.printClass();
    }
}

Ausgabe davon wird sein

In C Class
In A Class

Das Druckklassenverhalten der Klasse B wird in diesem Fall ignoriert. Ich bin mir nicht sicher, ob dies eine ideale oder gute Praxis ist, um super.super zu erreichen, aber es funktioniert immer noch.


1
Das ist kreativ, beantwortet aber meine Frage nicht wirklich. C nennt immer noch nicht super.super, B verhält sich einfach anders. Wenn Sie A und B ändern können, können Sie einfach eine andere Methode hinzufügen, anstatt instanceof zu verwenden. Super.super.foo würde Ihnen in Fällen helfen, in denen Sie keinen Zugriff auf A und B haben und diese nicht ändern können.
Tim Büthe

Stimmen Sie @TimButhe zu, aber wenn Sie super.super aufrufen möchten, möchten Sie das Verhalten der übergeordneten Klasse absichtlich ignorieren. Sie müssen dies also nur durch die vorhandene Java-Syntax erreichen. (Jede Option, die Sie entweder Instanz oder andere Methode wollen)
Sanjay Jain

0

Wenn Sie glauben, dass Sie die Oberklasse benötigen, können Sie sie in einer Variablen für diese Klasse referenzieren. Zum Beispiel:

public class Foo
{
  public int getNumber()
  {
    return 0;
  }
}

public class SuperFoo extends Foo
{
  public static Foo superClass = new Foo();
  public int getNumber()
  {
    return 1;
  }
}

public class UltraFoo extends Foo
{
  public static void main(String[] args)
  {
    System.out.println(new UltraFoo.getNumber());
    System.out.println(new SuperFoo().getNumber());
    System.out.println(new SuperFoo().superClass.getNumber());
  }
  public int getNumber()
  {
    return 2;
  }
}

Sollte ausdrucken:

2
1
0

2
Ihr Beispiel ist irgendwie ... gut schlecht, weil Sie statische Methoden verwenden. Wenn Sie statische Methoden verwenden, benötigen Sie die Variable oder Super überhaupt nicht. Vielleicht haben Sie hier ein grundlegendes OO-Konzept verpasst. Lesen Sie sich das unbedingt durch. Ich muss abstimmen, sorry.
Tim Büthe

1
Ohne statische Methoden wäre dies leicht möglich gewesen. Es ist einfach genug. Ich wollte ein einfaches Beispiel dafür finden.
Ashtheking

Ich verstehe, dass das Speichern der Supervariablen in einem Feld eine Möglichkeit ist, dies zu lösen. Sie benötigen die Variable jedoch nicht, wenn es sich um eine statische Methode handelt. Sie können sie einfach verwenden. Zweitens ist das Aufrufen statischer Methoden für eine Variable eine schlechte Praxis, und die meisten IDEs warnen Sie davor. Wenn Sie Ihre Antwort korrigieren und das statische Material entfernen, würde ich gerne meine Ablehnung entfernen, keine Beleidigung.
Tim Büthe

Sie sind nah dran, aber Ihr Code wird nicht kompiliert. Sie versuchen, in Ihrer Hauptmethode aus einem statischen Kontext auf getNumber zuzugreifen. Haben Sie tatsächlich versucht, dies zu kompilieren? (Und sollte Ihr UltraFoo SuperFoo nicht verlängern?)
Tim Büthe

Ich möchte wirklich nicht grausam gegenüber dir sein, werde aber new UltraFoo.getNumber()nicht kompilieren, da du dort Klammern verpasst hast. Ich habe jedoch gerade meine Donvote entfernt, da das Konzept Ihres Codes jetzt ziemlich klar ist, danke!
Tim Büthe

0

IMO, es ist ein sauberer Weg, um super.super.sayYourName()Verhalten in Java zu erreichen .

public class GrandMa {  
    public void sayYourName(){  
        System.out.println("Grandma Fedora");  
    }  
}  

public class Mama extends GrandMa {  
    public void sayYourName(boolean lie){  
        if(lie){   
            super.sayYourName();  
        }else {  
            System.out.println("Mama Stephanida");  
        }  
    }  
}  

public class Daughter extends Mama {  
    public void sayYourName(boolean lie){  
        if(lie){   
            super.sayYourName(lie);  
        }else {  
            System.out.println("Little girl Masha");  
        }  
    }  
}  

public class TestDaughter {
    public static void main(String[] args){
        Daughter d = new Daughter();

        System.out.print("Request to lie: d.sayYourName(true) returns ");
        d.sayYourName(true);
        System.out.print("Request not to lie: d.sayYourName(false) returns ");
        d.sayYourName(false);
    }
}

Ausgabe:

Request to lie: d.sayYourName(true) returns Grandma Fedora
Request not to lie: d.sayYourName(false) returns Little girl Masha


ah, also befürworten Sie die Implementierung einer solchen Klassenhierarchie? Leider wird dies sehr chaotisch, wenn Sie auf die Methode in Mama von Baby (Tochter Unterklasse) zugreifen möchten ...
bcr

Yakov Fain ist richtig. Mit anderen Worten, es ist kein gutes Beispiel, da die ursprüngliche Frage den Aufruf einer überschriebenen Methode in super.super betraf.
Inor

0

Ich denke, dies ist ein Problem, das die Erbschaftsvereinbarung bricht.
Indem Sie eine Klasse erweitern, gehorchen Sie deren Verhalten und Funktionen.
Während Sie anrufen super.super.method(), möchten Sie Ihre eigene Gehorsamvereinbarung brechen.

Sie können einfach nicht aus der Superklasse auswählen .

Es kann jedoch vorkommen, dass Sie anrufen müssen super.super.method()- normalerweise ein schlechtes Designzeichen, in Ihrem Code oder in dem Code, den Sie erben!
Wenn die Super- und Super-Super- Klassen nicht überarbeitet werden können (etwas Legacy-Code), entscheiden Sie sich für Komposition statt Vererbung.

Das Aufbrechen der Kapselung erfolgt, wenn Sie einige Methoden überschreiben , indem Sie den gekapselten Code aufheben . Die Methoden, die nicht überschrieben werden sollen, sind als endgültig markiert .


0

In C # können Sie eine Methode eines beliebigen Vorfahren wie folgt aufrufen:

public class A
    internal virtual void foo()
...
public class B : A
    public new void foo()
...
public class C : B
    public new void foo() {
       (this as A).foo();
    }

Dies können Sie auch in Delphi tun:

type
   A=class
      procedure foo;
      ...
   B=class(A)
     procedure foo; override;
     ...
   C=class(B)
     procedure foo; override;
     ...
A(objC).foo();

In Java können Sie einen solchen Fokus jedoch nur mit bestimmten Geräten erzielen. Ein möglicher Weg ist:

class A {               
   int y=10;            

   void foo(Class X) throws Exception {  
      if(X!=A.class)
         throw new Exception("Incorrect parameter of "+this.getClass().getName()+".foo("+X.getName()+")");
      y++;
      System.out.printf("A.foo(%s): y=%d\n",X.getName(),y);
   }
   void foo() throws Exception { 
      System.out.printf("A.foo()\n");
      this.foo(this.getClass()); 
   }
}

class B extends A {     
   int y=20;            

   @Override
   void foo(Class X) throws Exception { 
      if(X==B.class) { 
         y++; 
         System.out.printf("B.foo(%s): y=%d\n",X.getName(),y);
      } else { 
         System.out.printf("B.foo(%s) calls B.super.foo(%s)\n",X.getName(),X.getName());
         super.foo(X);
      } 
   }
}

class C extends B {     
   int y=30;            

   @Override
   void foo(Class X) throws Exception { 
      if(X==C.class) { 
         y++; 
         System.out.printf("C.foo(%s): y=%d\n",X.getName(),y);
      } else { 
         System.out.printf("C.foo(%s) calls C.super.foo(%s)\n",X.getName(),X.getName());
         super.foo(X);
      } 
   }

   void DoIt() {
      try {
         System.out.printf("DoIt: foo():\n");
         foo();         
         Show();

         System.out.printf("DoIt: foo(B):\n");
         foo(B.class);  
         Show();

         System.out.printf("DoIt: foo(A):\n");
         foo(A.class);  
         Show();
      } catch(Exception e) {
         //...
      }
   }

   void Show() {
      System.out.printf("Show: A.y=%d, B.y=%d, C.y=%d\n\n", ((A)this).y, ((B)this).y, ((C)this).y);
   }
} 

Ergebnisausgabe von objC.DoIt ():

DoIt: foo():
A.foo()
C.foo(C): y=31
Show: A.y=10, B.y=20, C.y=31

DoIt: foo(B):
C.foo(B) calls C.super.foo(B)
B.foo(B): y=21
Show: A.y=10, B.y=21, C.y=31

DoIt: foo(A):
C.foo(A) calls C.super.foo(A)
B.foo(A) calls B.super.foo(A)
A.foo(A): y=11
Show: A.y=11, B.y=21, C.y=31

In C # funktioniert es nur für nicht virtuelle Methoden, und da alle Methoden in Java virtuell sind, ist es nicht wirklich anders.
Agent_L

0

Es ist einfach einfach zu machen. Zum Beispiel:

C-Unterklasse von B und B-Unterklasse von A. Beide drei haben beispielsweise die Methode methodName ().

public abstract class A {

    public void methodName() {
        System.out.println("Class A");
    }

}

public class B extends A {

    public void methodName() {
        super.methodName();
        System.out.println("Class B");
    }

    // Will call the super methodName
    public void hackSuper() {
        super.methodName();
    }

}

public class C extends B {

    public static void main(String[] args) {
        A a = new C();
        a.methodName();
    }

    @Override
    public void methodName() {
        /*super.methodName();*/
        hackSuper();
        System.out.println("Class C");
    }

}

Laufklasse C Die Ausgabe lautet: Klasse A Klasse C.

Anstelle von Ausgabe: Klasse A Klasse B Klasse C.


-1
public class SubSubClass extends SubClass {

    @Override
    public void print() {
        super.superPrint();
    }

    public static void main(String[] args) {
        new SubSubClass().print();
    }
}

class SuperClass {

    public void print() {
        System.out.println("Printed in the GrandDad");
    }
}

class SubClass extends SuperClass {

    public void superPrint() {
        super.print();
    }
}

Ausgabe: Gedruckt im GrandDad


2
Diese Antwort liegt außerhalb des Rahmens der Frage. Das OP fragte nicht, wie man eine Methode in einer Großelternklasse aufruft. Das Problem ist eine Diskussion darüber, warum super.super.method()in Java kein gültiger Code vorhanden ist.
Jed Schaaf

-1

Das Schlüsselwort super ist nur eine Möglichkeit, die Methode in der Oberklasse aufzurufen. Im Java-Tutorial: https://docs.oracle.com/javase/tutorial/java/IandI/super.html

Wenn Ihre Methode eine der Methoden ihrer Oberklasse überschreibt, können Sie die überschriebene Methode mithilfe des Schlüsselworts super aufrufen.

Glaube nicht, dass es eine Referenz des Superobjekts ist !!! Nein, es ist nur ein Schlüsselwort, um Methoden in der Oberklasse aufzurufen.

Hier ist ein Beispiel:

class Animal {
    public void doSth() {
        System.out.println(this);   // It's a Cat! Not an animal!
        System.out.println("Animal do sth.");
    }
}

class Cat extends Animal {
    public void doSth() {
        System.out.println(this);
        System.out.println("Cat do sth.");
        super.doSth();
    }
}

Wenn Sie aufrufen cat.doSth(), wird die Methode doSth()in der Klasse Animalgedruckt thisund es handelt sich um eine Katze.

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.