Perché super.super.method (); non consentito in Java?


360

Ho letto questa domanda e ho pensato che sarebbe stato risolto facilmente (non che non fosse risolvibile senza) se si potesse scrivere:

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

Non sono sicuro che sia utile in molti casi, ma mi chiedo perché non lo sia e se qualcosa del genere esiste in altre lingue.

Che cosa ne pensate?

EDIT: Per chiarire: sì, lo so, è impossibile in Java e non mi manca davvero. Questo non è nulla che mi aspettassi di funzionare ed è stato sorpreso di ottenere un errore del compilatore. Ho appena avuto l'idea e mi piace discuterne.


7
Il voler chiamare super.super.toString()contraddice la tua decisione quando scegli di estendere una classe accettando così tutte (non alcune) delle sue caratteristiche.
DayaMoon

Risposte:


484

Viola l'incapsulamento. Non dovresti essere in grado di aggirare il comportamento della classe genitore. Ha senso essere a volte in grado di bypassare il vostro proprio comportamento della classe (in particolare da dentro lo stesso metodo), ma non è il tuo genitore. Ad esempio, supponiamo di avere una "raccolta di oggetti" di base, una sottoclasse che rappresenta "una raccolta di oggetti rossi" e una sottoclasse di quella che rappresenta "una raccolta di grandi oggetti rossi". Ha senso avere:

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

Va bene - RedItems può sempre essere sicuro che gli elementi che contiene sono tutti rossi. Supponiamo ora di stati in grado di chiamare super.super.add ():

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

Ora potremmo aggiungere quello che ci piace e l'invariante RedItemsè rotto.

Ha senso?


38
bell'esempio. ma ho pensato che fosse una cattiva progettazione quando la classe base accetta oggetti, ma la classe derivata li rifiuta, perché il derivato non può essere usato come rimpiazzo di sostituzione per la classe base (violazione del principio di sostituzione). è giusto pensare o una gerarchia del genere è pulita?
Johannes Schaub - litb

5
Intendi la composizione sull'eredità? Questo esempio non è un motivo per evitare l'ereditarietà, sebbene ce ne siano molti altri.
Jon Skeet,

3
@Tim: questo era solo un esempio di facile comprensione della violazione dell'incapsulamento. Potrebbe invece impostare una proprietà. Inoltre, non tutti gli aspetti saranno visibili a livello di tipo. Generics non è la risposta a tutto.
Jon Skeet,

12
@ JohannesSchaub-litb Penso che violi il principio di sostituzione di Liskov, poiché se si codifica il contratto di Item e si utilizza un'istanza di RedItems, si noterà NotRedItemException inaspettato. Mi è stato sempre insegnato che una sottoclasse dovrebbe prendere un super set di input e restituire un sottoinsieme di output. In altre parole, una sottoclasse non dovrebbe mai rifiutare un input valido per la superclasse o produrre un output che la superclasse non può produrre, questo include il lancio di un errore che la superclasse non genera.
Konstantin Tarashchanskiy,

4
@piechuckerr: a volte libri, a volte post di blog, a volte solo esperienza ...
Jon Skeet,

72

Penso che Jon Skeet abbia la risposta corretta. Vorrei solo aggiungere che puoi accedere alle variabili ombreggiate dalle superclassi di superclassi eseguendo il 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();
        }
}

che produce l'output:

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

(esempio dal JLS )

Tuttavia, ciò non funziona per le chiamate di metodo perché le chiamate di metodo sono determinate in base al tipo di runtime dell'oggetto.


6
Se hai una variabile con lo stesso nome di una in una superclasse e per qualche motivo non puoi (o non ti è permesso) cambiarla, puoi comunque accedere alla variabile della superclasse con lo stesso nome. A che serve, chiedi? Beh ... non l'ho mai usato.
Michael Myers

Ottimo collegamento al documento delle espressioni. Alcuni begli esempi per le persone che studiano per l'OCCPJP.
Gordon,

Eccezionale. Non avrei mai pensato di usare un cast.
Thomas Eding,

come mai la classe T1 ha la precedenza sulla variabile x? il suo finale statico di default giusto?
amarnath harish,

@amarnathharish: non viene sovrascritto e i campi sono protetti per impostazione predefinita dal pacchetto.
Michael Myers

41

Penso che il seguente codice consenta di utilizzare super.super ... super.method () nella maggior parte dei casi. (anche se è brutto farlo)

In breve

  1. creare un'istanza temporanea di tipo antenato
  2. copia i valori dei campi dall'oggetto originale a uno temporaneo
  3. invoca il metodo target su oggetto temporaneo
  4. copia i valori modificati sull'oggetto originale

Utilizzo:

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
Con la riflessione puoi fare qualsiasi cosa, sì :) Puoi persino rendere le stringhe mutabili.
BalusC

23
Che cosa orribile da fare! Ti sto dando +1 solo per capire come farlo :) :)
Larry Watanabe,

6
È un bel trucco, ma anche questo non equivale sempre a chiamare il super.super non disponibile, ma necessario, ed è perché la chiamata super.super porterebbe il contesto di C (C + B + A) mentre le tue risposte creano un'istanza di A senza il contesto di B e C. Quindi questa risposta non funzionerà se ogni doThat chiamato getContext (), per esempio, e getContext fosse implementato in modo diverso in ogni classe. nella tua risposta userebbe A's getContext () mentre la chiamata del super.super non disponibile avrebbe comportato l'uso di C getContext.
o il

Hmm. In alcuni casi, potresti forse superare l'obiezione di inor con proxy dinamici ( javahowto.blogspot.co.uk/2011/12/… ), reindirizzando le chiamate del metodo all'oggetto originale (sincronizzando le variabili dopo ogni chiamata)? Sembra però che i proxy richiedano tutto per implementare un'interfaccia. Inoltre, mi chiedo se è possibile per la classe super-super chiamare uno dei suoi metodi super-super, in particolare, e non avresti bisogno di reindirizzare quelli ....
Erhannis

Ho detto in un altro commento che il blocco super.super.invita i programmatori a trovare modi nuovi, contorti ed egregi per spararsi ai piedi nella ricerca di una soluzione alternativa, questo è un esempio perfetto di ciò, perché i tuoi colleghi probabilmente ti odieranno così tanto per aver scritto qualcosa così che ti spareranno personalmente e letteralmente ai piedi. +1
Braden Best

11

Non ho abbastanza reputazione per commentare, quindi lo aggiungerò alle altre risposte.

Jon Skeet risponde in modo eccellente, con un bellissimo esempio. Matt B ha un punto: non tutte le superclasse hanno supers. Il tuo codice si spezzerebbe se tu chiamassi un super di un super che non aveva super.

La programmazione orientata agli oggetti (che è Java) riguarda gli oggetti, non le funzioni. Se desideri una programmazione orientata alle attività, scegli C ++ o qualcos'altro. Se il tuo oggetto non rientra nella sua super classe, allora devi aggiungerlo alla "classe dei nonni", creare una nuova classe o trovare un'altra super adatta.

Personalmente, ho trovato questa limitazione come uno dei maggiori punti di forza di Java. Il codice è un po 'rigido rispetto ad altre lingue che ho usato, ma so sempre cosa aspettarmi. Questo aiuta con l'obiettivo "semplice e familiare" di Java. Nella mia mente, chiamare super.super non è semplice o familiare. Forse gli sviluppatori hanno provato lo stesso?


3
Dici "non tutte le superclassi hanno supers". Bene, tranne java.lang.Object cosa potrebbe darti "null". Quindi direi che quasi tutti hanno delle cene.
Tim Büthe

Ogni classe il programmatore applicazione scrive ha un "super" (java.lang.Object non lo fa, ma il programmatore applicazione non scrivere che.)
finnw

2
Facilmente risolto rendendo super.super.super ... super un errore di compilazione se ci sono troppi super. Considerando che Java ha solo eredità pubblica, se qualcuno ha cambiato la gerarchia dell'ereditarietà, stai cambiando l'interfaccia. Quindi non sarei preoccupato che super ^ n sia spaventoso.
Thomas Eding,

7

Ci sono alcuni buoni motivi per farlo. È possibile che si disponga di una sottoclasse con un metodo implementato in modo errato, ma il metodo padre è implementato correttamente. Poiché appartiene a una libreria di terze parti, potresti non essere in grado / non disposto a cambiare l'origine. In questo caso, si desidera creare una sottoclasse ma sovrascrivere un metodo per chiamare il metodo super.super.

Come mostrato da alcuni altri poster, è possibile farlo attraverso la riflessione, ma dovrebbe essere possibile fare qualcosa di simile

(SuperSuperClass this) .theMethod ();

Sto affrontando questo problema in questo momento - la soluzione rapida è copiare e incollare il metodo superclasse nel metodo subsubclass :)


1
Il cast prima di chiamare un metodo non cambia il metodo a cui è delegato: viene sempre utilizzata l'implementazione della sottoclasse, mai quella dei super. Vedi, ad esempio, stackoverflow.com/questions/1677993/…
Joshua Goldberg

1
@Larry, questa è esattamente la situazione in cui mi trovavo ed esattamente la correzione che ho usato. Ottima scelta!
bcr,

Se si dispone del codice del metodo necessario, è possibile aprire tutti i campi necessari ed eseguire questo codice nella sottoclasse.
Enyby,

6

Oltre ai punti molto positivi che altri hanno fatto, penso che ci sia un altro motivo: cosa succede se la superclasse non ha una superclasse?

Poiché ogni classe si estende naturalmente (almeno) Object, super.whatever()farà sempre riferimento a un metodo nella superclasse. Ma cosa succede se la tua classe si estende solo Object- a cosa farebbe super.superriferimento allora? Come dovrebbe essere gestito quel comportamento: un errore del compilatore, un NullPointer, ecc.?

Penso che il motivo principale per cui ciò non è consentito sia la violazione dell'incapsulamento, ma questo potrebbe essere anche un piccolo motivo.


4
Ovviamente, sarebbe un errore del compilatore - ed è l'informazione che ha il compilatore.
Michael Borgwardt,

4

Penso che se si sovrascrive un metodo e si desidera tutta la equalssua versione superclasse (come, diciamo per ), allora si vuole praticamente sempre chiamare prima la versione della superclasse diretta, che si chiamerà a sua volta la sua versione della superclasse se lo si desidera .

Penso che abbia raramente senso (se non del tutto. Non riesco a pensare a un caso in cui lo faccia) chiamare una versione di un metodo di una superclasse arbitraria. Non so se ciò sia possibile in Java. Può essere fatto in C ++:

this->ReallyTheBase::foo();

6
Ha senso se qualcuno ha scritto una sottoclasse con un metodo implementato in modo errato ma il metodo superclasse svolge il 90% del lavoro. Quindi si desidera creare una sottoclasse e sovrascrivere il metodo chiamando il metodo superclasse superclasse e aggiungere il proprio 10%.
Larry Watanabe,

3

A indovinare, perché non viene usato così spesso. L'unico motivo per cui ho potuto vederlo è se il tuo genitore diretto ha ignorato alcune funzionalità e stai provando a ripristinarlo all'originale.

Il che mi sembra contrario ai principi di OO, dal momento che il genitore diretto della classe dovrebbe essere più strettamente legato alla tua classe di quanto non lo sia il nonno.


3

Guarda questo progetto Github, in particolare la variabile objectHandle. Questo progetto mostra come chiamare effettivamente e accuratamente il metodo del nonno su un nipote.

Nel caso in cui il collegamento venga interrotto, ecco il codice:

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

Buona programmazione !!!!


I tuoi scienziati erano così preoccupati che potessero o no, non si sono fermati a pensare se avrebbero dovuto. Per favore, non farlo ...: P 🤔
Tim Büthe,

Sì, quelle sono le parole del programmatore, non le mie. Secondo me, penso che potresti averne effettivamente bisogno un giorno
kyay

2

Metterei il corpo del metodo super.super in un altro metodo, se possibile

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

Oppure, se non puoi cambiare la classe super-super, puoi provare questo:

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 entrambi i casi, il

new ChildClass().toString();

risultati a "I am super super"


Mi sono appena trovato in una situazione in cui possiedo SuperSuperClass e ChildClass ma non SuperClass, quindi ho trovato utile la prima soluzione.
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 (tempo totale: 0 secondi)


1
Vedo, ma stai creando una nuova istanza, quindi non funzionerebbe se l'oggetto ha uno stato.
Tim Büthe

1

La chiamata di super.super.method () ha senso quando non è possibile modificare il codice della classe base. Ciò accade spesso quando si estende una libreria esistente.

Chiediti prima, perché stai estendendo quella classe? Se la risposta è "perché non posso cambiarla", puoi creare un pacchetto e una classe esatti nella tua applicazione e riscrivere il metodo cattivo o creare un delegato:

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() {...}
    ...
}

Ad esempio, è possibile creare org.springframework.test.context.junit4.SpringJUnit4ClassRunner nella propria applicazione, quindi questa classe dovrebbe essere caricata prima di quella reale dal jar. Quindi riscrivere metodi o costruttori.

Attenzione: questo è un trucco assoluto, e NON è consigliabile utilizzarlo ma FUNZIONA! L'uso di questo approccio è pericoloso a causa di possibili problemi con i caricatori di classi. Inoltre, ciò può causare problemi ogni volta che si aggiorna la libreria che contiene classe sovrascritta.


1

Ho avuto situazioni come queste quando l'architettura deve costruire funzionalità comuni in una CustomBaseClass comune che implementa per conto di diverse classi derivate. Tuttavia, dobbiamo aggirare la logica comune per un metodo specifico per una specifica classe derivata. In questi casi, dobbiamo usare un'implementazione super.super.methodX.

Raggiungiamo questo obiettivo introducendo un membro booleano in CustomBaseClass, che può essere utilizzato per differire selettivamente l'implementazione personalizzata e cedere all'implementazione del framework predefinita, ove desiderabile.

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

Tuttavia, con i buoni principi dell'architettura seguiti nel framework e nell'app, potremmo evitare facilmente tali situazioni, usando l'approccio hasA anziché l'approccio isA. Ma in ogni momento non è molto pratico aspettarsi un'architettura ben progettata, e quindi la necessità di allontanarsi da solidi principi di progettazione e introdurre hack come questo. Solo i miei 2 centesimi ...


1

@Jon Skeet Bella spiegazione. IMO se qualcuno vuole chiamare il metodo super.super, allora si deve voler ignorare il comportamento del genitore immediato, ma si desidera accedere al comportamento del nonno genitore. Ciò può essere ottenuto tramite l'istanza di. Come sotto il codice

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

Ecco la classe di guida,

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

L'output di questo sarà

In C Class
In A Class

In questo caso il comportamento printClass di classe B. verrà ignorato. Non sono sicuro che sia una pratica ideale o buona per raggiungere il super.super, ma funziona ancora.


1
Bene, è creativo ma non risponde davvero alla mia domanda. C non chiama ancora super.super, B si comporta in modo diverso. Se puoi cambiare A e B, puoi semplicemente aggiungere un altro metodo invece di usare instanceof. Super.super.foo ti aiuterebbe nei casi in cui non hai accesso ad A e B e non potresti cambiarli.
Tim Büthe

Accetto @ TimButhe, ma se si desidera chiamare a super.super, allora si vuole intenzionalmente ignorare il comportamento della classe genitore, quindi è sufficiente raggiungere tale obiettivo mediante la sintassi esistente di Java. (Qualunque opzione tu voglia in entrambi i casi / metodo diverso)
Sanjay Jain

0

Se ritieni di aver bisogno della superclasse, puoi fare riferimento in una variabile per quella classe. Per esempio:

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

Dovrebbe stampare:

2
1
0

2
Il tuo esempio è un po '... male, perché usi metodi statici. Quando si utilizzano metodi statici, non è necessaria la variabile o super. Forse ti sei perso alcuni concetti OO di base qui, assicurati di leggere su quello. Devo votare, scusa.
Tim Büthe

1
Avrebbe potuto essere facilmente fatto senza metodi statici. È abbastanza semplice Stavo per fare un semplice esempio su come farlo.
Ashtheking,

Capisco, archiviare la super variabile in un campo è un modo per risolverlo. Ma non è necessaria la variabile se si tratta di un metodo statico, è possibile utilizzarla. In secondo luogo, invocare metodi statici uno su una variabile è una cattiva pratica e la maggior parte degli IDE ti avvertirà al riguardo. Se risolvi la tua risposta, rimuovendo la roba statica, sarei felice di rimuovere il mio voto negativo, senza offesa.
Tim Büthe

Sei vicino, ma il tuo codice non verrà compilato. Si tenta di accedere a getNumber da un contesto statico nel metodo principale. Hai davvero provato a compilare questo? (e il tuo UltraFoo non dovrebbe estendere SuperFoo?)
Tim Büthe, il

Non voglio davvero essere crudele con te, ma new UltraFoo.getNumber()non mi compilerò, dato che hai perso le parentesi lì. Tuttavia, ho appena rimosso il mio donvote, poiché il concetto del tuo codice è abbastanza chiaro ora, grazie!
Tim Büthe

0

IMO, è un modo pulito per raggiungere super.super.sayYourName() comportamenti in Java.

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

Produzione:

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


ah, quindi stai sostenendo di implementare una gerarchia di classi come questa? Sfortunatamente, questo inizia a diventare davvero confuso se vuoi accedere al metodo in Mama da Baby (la sottoclasse di Daughter) ...
bcr

yakov fain ha ragione. in altre parole, non è un buon esempio, perché la domanda originale era di chiamare un metodo ignorato in super.super.
o il

0

Penso che questo sia un problema che rompe l'accordo di eredità.
Estendendo una classe obbedisci / accetti il ​​suo comportamento, caratteristiche
Mentre chiami super.super.method(), vuoi rompere il tuo accordo di obbedienza.

Non puoi semplicemente scegliere una ciliegia dalla super classe .

Tuttavia, possono verificarsi situazioni in cui senti la necessità di chiamare super.super.method(), di solito un segno di design errato, nel tuo codice o nel codice che erediti!
Se le classi super e super super non possono essere rifattorizzate (alcuni codici legacy), optare per la composizione rispetto all'eredità.

L' interruzione dell'incapsulamento avviene quando @Override alcuni metodi rompendo il codice incapsulato. I metodi progettati per non essere sovrascritti sono contrassegnati come finali .


0

In C # puoi chiamare un metodo di qualsiasi antenato come questo:

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

Inoltre puoi farlo in Delphi:

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

Ma in Java puoi focalizzare tale attenzione solo con alcuni attrezzi. Un modo possibile è:

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

risultato output 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 # funzionerà solo con metodi non virtuali e poiché tutti i metodi sono virtuali in Java non è molto diverso.
Agent_L

0

È semplicemente facile da fare. Per esempio:

Sottoclasse C di B e sottoclasse B di A. Entrambe le tre hanno il metodo methodName () per esempio.

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

}

Esegui classe C L'output sarà: Classe A Classe C

Invece dell'output: Classe A Classe B Classe 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();
    }
}

Output: stampato nel GrandDad


2
Questa risposta non rientra nell'ambito della domanda. L'OP non ha chiesto come chiamare un metodo in una classe di nonni. Il problema è una discussione sul perché super.super.method()non è un codice valido in Java.
Jed Schaaf,

-1

La parola chiave super è solo un modo per invocare il metodo nella superclasse. Nel tutorial Java: https://docs.oracle.com/javase/tutorial/java/IandI/super.html

Se il tuo metodo ha la precedenza su uno dei metodi della sua superclasse, puoi invocare il metodo sovrascritto mediante l'uso della parola chiave super.

Non credere che sia un riferimento al super oggetto !!! No, è solo una parola chiave per invocare metodi nella superclasse.

Ecco un esempio:

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

Quando chiami cat.doSth(), il metodo doSth()in classe Animalverrà stampato thised è un gatto.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.