426 Stimmen

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

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

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

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

Was haltet ihr davon?

EDITAR: Um das klarzustellen: Ja, ich weiß, das ist in Java unmöglich und ich vermisse es nicht wirklich. Ich habe nicht erwartet, dass das funktioniert und war überrascht, dass ich einen Compilerfehler bekomme. Ich hatte nur die Idee und möchte sie gerne diskutieren.

540voto

Jon Skeet Punkte 1325502

Das verstößt gegen die Verkapselung. Man sollte nicht in der Lage sein, das Verhalten der übergeordneten Klasse zu umgehen. Manchmal ist es sinnvoll, das Verhalten der eigenen Klasse zu umgehen. eigene Verhalten der Klasse (insbesondere innerhalb derselben Methode), nicht aber das der Elternklasse. Nehmen wir zum Beispiel an, wir haben eine Basisklasse "Sammlung von Gegenständen", eine Unterklasse, die "eine Sammlung von roten Gegenständen" darstellt und eine Unterklasse davon, die "eine Sammlung von großen roten Gegenständen" darstellt. Es macht Sinn, 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 sich immer darauf verlassen, dass die darin enthaltenen Elemente alle rot sind. Nehmen wir nun an, wir waren in der Lage, 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 in RedItems gebrochen ist.

Ergibt das einen Sinn?

89voto

Michael Myers Punkte 183216

Ich glaube, Jon Skeet hat die richtige Antwort. Ich möchte nur hinzufügen, dass Sie puede Zugriff auf Shadowed-Variablen von Superklassen von Superklassen durch Casting this :

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();
        }
}

die die Ausgabe erzeugt:

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

(Beispiel aus dem JLS )

Dies funktioniert jedoch nicht für Methodenaufrufe, da Methodenaufrufe auf der Grundlage des Laufzeittyps des Objekts bestimmt werden.

48voto

Nico Punkte 489

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

Kurz gesagt

  1. temporäre Instanz des Vorgängertyps erzeugen
  2. die Werte der Felder von Original Objekt zum temporären Objekt
  3. Aufrufen der Zielmethode für ein temporäres Objekt
  4. geänderte Werte in das Originalobjekt zurückkopieren

Verwendung:

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);
    }
}

12voto

EllaJo Punkte 225

Ich habe nicht genug Ansehen, um einen Kommentar abzugeben, also füge ich dies den anderen Antworten hinzu.

Jon Skeet antwortet in hervorragender Weise mit einem schönen Beispiel. Matt B hat einen Punkt: nicht alle Superklassen haben Supers. Ihr Code würde brechen, wenn Sie einen Super eines Super aufrufen, der keinen Super hat.

Bei der objektorientierten Programmierung (zu der Java gehört) dreht sich alles um Objekte, nicht um Funktionen. Wenn Sie aufgabenorientiert programmieren wollen, wählen Sie C++ oder etwas anderes. Wenn Ihr Objekt nicht in seine Superklasse passt, dann müssen Sie es der "Großelternklasse" hinzufügen, eine neue Klasse erstellen oder eine andere Superklasse finden, in die es passt.

Ich persönlich halte diese Einschränkung für eine der größten Stärken von Java. Der Code ist im Vergleich zu anderen Sprachen, die ich verwendet habe, etwas starr, aber ich weiß immer, was ich zu erwarten habe. Das hilft bei dem Ziel "einfach und vertraut" von Java. Meiner Meinung nach ist der Aufruf von super.super weder einfach noch vertraut. Vielleicht haben die Entwickler das auch so gesehen?

10voto

Larry Watanabe Punkte 9916

Dafür gibt es einige gute Gründe. Es kann sein, dass eine Unterklasse eine Methode hat, die falsch implementiert ist, aber die übergeordnete Methode ist korrekt implementiert. Da sie zu einer Bibliothek eines Drittanbieters gehört, sind Sie möglicherweise nicht in der Lage/willens, den Quellcode zu ändern. In diesem Fall möchten Sie eine Unterklasse erstellen, aber eine Methode überschreiben, um die super.super-Methode aufzurufen.

Wie von einigen anderen Postern gezeigt, ist es möglich, dies durch Reflexion zu tun, aber es sollte möglich sein, etwas zu tun wie

(SuperSuperClass this).theMethod();

Ich beschäftige mich gerade mit diesem Problem - die schnelle Lösung ist, die Methode der Oberklasse in die Methode der Unterklasse zu kopieren und einzufügen :)

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X