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.

6voto

matt b Punkte 135206

Zusätzlich zu den sehr guten Argumenten, die andere vorgebracht haben, gibt es meiner Meinung nach noch einen weiteren Grund: Was ist, wenn die Superklasse keine Superklasse hat?

Da jede Klasse natürlich (mindestens) Object , super.whatever() verweist immer auf eine Methode in der Oberklasse. Was aber, wenn Ihre Klasse nur die Object - Was würde super.super beziehen sich dann auf? Wie sollte dieses Verhalten behandelt werden - ein Compilerfehler, ein NullPointer usw.?

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

5voto

Ich denke, wenn Sie eine Methode überschreiben und alle die Superklasse Version von ihm (wie, sagen wir für equals ), dann sollten Sie praktisch immer zuerst die direkte Oberklassenversion aufrufen, die ihrerseits ihre Oberklassenversion aufrufen wird, wenn sie das möchte.

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

this->ReallyTheBase::foo();

3voto

ruruskyi Punkte 1931

Der Aufruf von super.super.method() ist sinnvoll, wenn Sie den Code der Basisklasse nicht ändern können. Dies ist häufig der Fall, wenn Sie eine bestehende Bibliothek erweitern.

Fragen Sie sich zunächst, warum Sie diese Klasse erweitern? Wenn die Antwort lautet: "Weil ich sie nicht ändern kann", dann können Sie ein genaues Paket und eine genaue Klasse in Ihrer Anwendung erstellen und die unanständige Methode umschreiben 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() {...}
    ...
}

Sie können zum Beispiel Folgendes erstellen org.springframework.test.context.junit4.SpringJUnit4ClassRunner Klasse in Ihrer Anwendung, so dass diese Klasse vor der eigentlichen Klasse aus jar geladen werden sollte. Schreiben Sie dann Methoden oder Konstruktoren um.

Achtung! Dies ist ein absoluter Hack, dessen Verwendung NICHT empfohlen wird, aber er funktioniert! Die Verwendung dieses Ansatzes ist wegen möglicher Probleme mit Klassenladern gefährlich. Außerdem kann dies jedes Mal Probleme verursachen, wenn Sie eine Bibliothek aktualisieren, die eine überschriebene Klasse enthält.

3voto

Sanjay Jain Punkte 3458

@Jon Skeet Gute Erklärung. IMO, wenn jemand die super.super-Methode aufrufen will, dann muss man das Verhalten des unmittelbaren Elternteils ignorieren wollen, aber auf das Verhalten des großen Elternteils zugreifen wollen. Dies kann durch die Instanz Of erreicht werden. Wie untenstehender 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 die Fahrerklasse,

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

Das Ergebnis wird sein

In C Class
In A Class

Das Verhalten der Klasse B printClass 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 trotzdem.

3voto

kyay10 Punkte 746

Blick auf este Github-Projekt, insbesondere die Variable objectHandle. Dieses Projekt zeigt, wie man die Methode grandparent für ein Enkelkind tatsächlich und genau aufruft.

Nur für den Fall, dass der Link nicht funktioniert, hier ist 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!!!!

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