Pourquoi est super.super.method (); pas autorisé en Java?

360

J'ai lu cette question et j'ai pensé que cela serait facilement résolu (pas qu'il ne soit pas résolu sans) si l'on pouvait écrire:

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

Je ne sais pas si c'est utile dans de nombreux cas, mais je me demande pourquoi ce n'est pas le cas et si quelque chose comme ça existe dans d'autres langues.

Qu'en pensez-vous?

EDIT: Pour clarifier: oui je sais, c'est impossible en Java et ça ne me manque pas vraiment. Ce n'est rien que je m'attendais à travailler et j'ai été surpris d'obtenir une erreur de compilation. J'ai juste eu l'idée et j'aimerais en discuter.

Tim Büthe
la source
7
Vouloir appeler super.super.toString()contredit votre propre décision lorsque vous choisissez d'étendre une classe, acceptant ainsi toutes (pas certaines) ses fonctionnalités.
DayaMoon

Réponses:

484

Il viole l'encapsulation. Vous ne devriez pas pouvoir contourner le comportement de la classe parent. Il est logique de pouvoir parfois contourner le comportement de votre propre classe (en particulier à partir de la même méthode) mais pas celui de vos parents. Par exemple, supposons que nous ayons une base "collection d'articles", une sous-classe représentant "une collection d'articles rouges" et une sous-classe de celle représentant "une collection de gros articles rouges". Il est logique d'avoir:

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

C'est bien - RedItems peut toujours être sûr que les éléments qu'il contient sont tous rouges. Supposons maintenant que nous avons été en mesure d'appeler 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);
    }
}

Maintenant, nous pourrions ajouter tout ce que nous voulons, et l'invariant dans RedItemsest rompu.

Cela a-t-il du sens?

Jon Skeet
la source
38
bel exemple. mais je pensais que c'était une mauvaise conception lorsque la classe de base accepte des éléments, mais la classe dérivée les rejette, car la dérivée ne peut pas être utilisée en remplacement de la classe de base (violation du principe de substitution). est-ce la bonne pensée, ou une telle hiérarchie est-elle propre?
Johannes Schaub - litb
5
Voulez-vous dire la composition sur l'héritage? Cet exemple n'est pas une raison pour éviter l'héritage - bien qu'il y en ait beaucoup d'autres.
Jon Skeet
3
@Tim: Ce n'était qu'un exemple facile à comprendre de violation de l'encapsulation. Il pourrait être en train de définir une propriété à la place. De plus, tous les aspects ne seront pas visibles au niveau du type. Les génériques ne sont pas la réponse à tout.
Jon Skeet
12
@ JohannesSchaub-litb Je pense que cela viole le principe de substitution Liskov, car si on code au contrat de Item et utilise une instance de RedItems, on obtiendra une NotRedItemException inattendue. On m'a toujours appris qu'une sous-classe devrait prendre un super ensemble d'entrées et renvoyer un sous-ensemble de sorties. En d'autres termes, une sous-classe ne doit jamais rejeter une entrée qui est valide pour la super classe ou produire une sortie que la super classe ne peut pas produire, cela inclut le lancement d'une erreur que la super classe ne lance pas.
Konstantin Tarashchanskiy
4
@piechuckerr: Parfois des livres, parfois des articles de blog, parfois juste de l'expérience ...
Jon Skeet
72

Je pense que Jon Skeet a la bonne réponse. Je voudrais juste ajouter que vous pouvez accéder aux variables ombrées à partir de superclasses de superclasses en transtypant 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();
        }
}

qui produit la sortie:

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

(exemple du JLS )

Cependant, cela ne fonctionne pas pour les appels de méthode, car les appels de méthode sont déterminés en fonction du type d'exécution de l'objet.

Michael Myers
la source
6
Si vous avez une variable du même nom que celle d'une superclasse et pour une raison quelconque, vous ne pouvez pas (ou n'êtes pas autorisé à) la modifier, vous pouvez toujours accéder à la variable de la superclasse avec le même nom. Quelle est l'utilité, demandez-vous? Eh bien ... je ne l'ai jamais utilisé.
Michael Myers
Excellent lien vers le document d'expressions. Quelques bons exemples pour les personnes étudiant à l'OCPJP.
Gordon
Impressionnant. Je n'aurais jamais pensé à utiliser un casting.
Thomas Eding,
comment se fait-il que la classe T1 remplace la variable x? sa finale statique par défaut non?
amarnath harish
@amarnathharish: il n'est pas remplacé, et les champs sont protégés par package non finaux par défaut.
Michael Myers
41

Je pense que le code suivant permet d'utiliser super.super ... super.method () dans la plupart des cas. (même si c'est moche de faire ça)

En bref

  1. créer une instance temporaire de type ancêtre
  2. copier les valeurs des champs de l' objet d' origine vers un objet temporaire
  3. invoquer la méthode cible sur un objet temporaire
  4. recopiez les valeurs modifiées dans l'objet d'origine

Utilisation:

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);
    }
}
Nico
la source
10
Avec la réflexion, vous pouvez tout faire, oui :) Vous pouvez même rendre les chaînes mutables.
BalusC
23
Quelle chose horrible à faire! Je vous donne +1 juste pour avoir compris comment le faire :)
Larry Watanabe
6
C'est une bonne astuce, mais même cela n'est pas toujours équivalent à appeler l'inévitable, mais pourtant nécessaire) super.super et c'est parce que l'appel super.super porterait le contexte de C (C + B + A) alors que vos réponses créent une instance de A sans le contexte de B et C. Donc, cette réponse ne fonctionnera pas si chaque doThat appelé getContext (), par exemple, et getContext a été implémenté différemment dans chaque classe. dans votre réponse, il utiliserait getContext () de A alors que l'appel du super.super indisponible aurait entraîné l'utilisation de getContext de C.
2012 à 9h55
Hmm. Dans certains cas, vous pourriez peut-être surmonter l'objection d'Inor avec des proxys dynamiques ( javahowto.blogspot.co.uk/2011/12/… ), rediriger les appels de méthode vers l'objet d'origine (synchroniser les variables après chaque appel)? Il semble que les procurations nécessitent tout pour implémenter une interface. De plus, je me demande s'il est possible pour la classe super-super d'appeler une de ses méthodes super-super, en particulier, et vous n'auriez pas besoin de les rediriger ....
Erhannis
J'ai dit dans un autre commentaire que le blocage super.super.invite les programmeurs à trouver de nouvelles façons compliquées et flagrantes de se tirer une balle dans le pied dans la poursuite d'une solution de contournement, ceci en est un parfait exemple, car vos collègues vous détesteront probablement tellement pour avoir écrit quelque chose comme ça, ils vous tireront personnellement et littéralement dans le pied. +1
Braden Best
11

Je n'ai pas assez de réputation pour commenter, je vais donc l'ajouter aux autres réponses.

Jon Skeet répond parfaitement, avec un bel exemple. Matt B a raison: toutes les superclasses n'ont pas de supers. Votre code se briserait si vous appeliez un super d'un super qui n'avait pas de super.

La programmation orientée objet (qui est Java) concerne les objets, pas les fonctions. Si vous voulez une programmation orientée tâche, choisissez C ++ ou autre chose. Si votre objet ne rentre pas dans sa super classe, vous devez l'ajouter à la "classe des grands-parents", créer une nouvelle classe ou trouver un autre super auquel il rentre.

Personnellement, j'ai trouvé cette limitation comme l'une des plus grandes forces de Java. Le code est quelque peu rigide par rapport aux autres langages que j'ai utilisés, mais je sais toujours à quoi m'attendre. Cela contribue à l'objectif "simple et familier" de Java. Dans mon esprit, appeler super.super n'est ni simple ni familier. Peut-être que les développeurs ont ressenti la même chose?

EllaJo
la source
3
Vous dites "toutes les superclasses n'ont pas de supers". Eh bien, tout sauf java.lang.Object ce qui pourrait vous donner "null". Je dirais donc que presque tous ont des supers.
Tim Büthe
Chaque classe que le programmeur d'application écrit a un "super" (java.lang.Object ne le fait pas, mais le programmeur d'application n'écrit pas cela.)
finnw
2
Résolu facilement en faisant super.super.super ... super une erreur de temps de compilation s'il y a trop de supers. Considérant que Java n'a qu'un héritage public, si quelqu'un a changé la hiérarchie d'héritage, vous changez l'interface. Je ne serais donc pas inquiet que super ^ n soit effrayant.
Thomas Eding,
7

Il y a de bonnes raisons de le faire. Vous pouvez avoir une sous-classe qui a une méthode qui n'est pas implémentée correctement, mais la méthode parent est implémentée correctement. Parce qu'elle appartient à une bibliothèque tierce, vous pourriez ne pas pouvoir / ne pas vouloir changer la source. Dans ce cas, vous souhaitez créer une sous-classe mais remplacer une méthode pour appeler la méthode super.super.

Comme le montrent certaines autres affiches, il est possible de le faire par la réflexion, mais il devrait être possible de faire quelque chose comme

(SuperSuperClass this) .theMethod ();

Je traite ce problème en ce moment - la solution rapide consiste à copier et coller la méthode de superclasse dans la méthode de sous-sous-classe :)

Larry Watanabe
la source
1
Le cast avant d'appeler une méthode ne change pas la méthode qui est déléguée à: c'est toujours l'implémentation de la sous-classe qui est utilisée, jamais celle des super. Voir, par exemple, stackoverflow.com/questions/1677993/…
Joshua Goldberg
1
@Larry, c'est exactement la situation dans laquelle j'étais et exactement le correctif que j'ai utilisé. Bon appel!
bcr
Si vous avez le code de la méthode nécessaire, vous pouvez ouvrir tous les champs nécessaires et exécuter ce code dans votre sous-classe.
Enyby
6

En plus des très bons points que d'autres ont soulevés, je pense qu'il y a une autre raison: que faire si la superclasse n'a pas de superclasse?

Puisque chaque classe s'étend naturellement (au moins) Object, super.whatever()fera toujours référence à une méthode dans la superclasse. Mais que se passe-t-il si votre classe ne fait que s'étendre Object- à quoi se super.superréférer alors? Comment ce comportement doit-il être géré - une erreur de compilation, un NullPointer, etc.?

Je pense que la principale raison pour laquelle cela n'est pas autorisé est qu'il viole l'encapsulation, mais cela pourrait aussi être une petite raison.

mat b
la source
4
Évidemment, ce serait une erreur du compilateur - et ce sont des informations que le compilateur possède.
Michael Borgwardt
4

Je pense que si vous écrasez une méthode et que vous voulez toute la version super-classe de celle-ci (comme, disons pour equals), alors vous voulez presque toujours appeler la version directe de la superclasse en premier, que l'on appellera sa version de superclasse à son tour si elle le veut .

Je pense que cela n'a que rarement de sens (voire pas du tout. Je ne peux pas penser à un cas où il le fait) d'appeler une version arbitraire d'une superclasse d'une méthode. Je ne sais pas si cela est possible du tout en Java. Cela peut être fait en C ++:

this->ReallyTheBase::foo();
Johannes Schaub - litb
la source
6
Cela a du sens si quelqu'un a écrit une sous-classe avec une méthode implémentée incorrectement mais la méthode de super-classe fait 90% du travail. Ensuite, vous voulez créer une sous-classe, remplacer la méthode appelant la méthode superclass superclass et ajouter vos propres 10%.
Larry Watanabe
3

À une supposition, parce que ce n'est pas souvent utilisé. La seule raison pour laquelle je pouvais voir l'utiliser est que votre parent direct a outrepassé certaines fonctionnalités et que vous essayez de les restaurer à leur version d'origine.

Ce qui me semble contraire aux principes OO, car le parent direct de la classe devrait être plus étroitement lié à votre classe que le grand-parent.

Powerlord
la source
3

Regardez ce projet Github, en particulier la variable objectHandle. Ce projet montre comment appeler réellement et précisément la méthode des grands-parents sur un petit-enfant.

Juste au cas où le lien serait rompu, voici le 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";
        }
    }
}

Happy Coding !!!!

kyay
la source
Vos scientifiques étaient tellement préoccupés de savoir s'ils le pouvaient ou non, ils n'ont pas cessé de penser s'ils le devaient. S'il vous plaît, ne faites pas ça ...: P 🤔
Tim Büthe
Oui, ce sont les mots du codeur, pas les miens. À mon avis, je pense que vous en aurez peut-être besoin un jour
kyay
2

Je mettrais le corps de la méthode super.super dans une autre méthode, si possible

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

Ou si vous ne pouvez pas changer la classe super-super, vous pouvez essayer ceci:

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

Dans les deux cas, le

new ChildClass().toString();

résultats à "je suis super super"

xMichal
la source
Je viens de me retrouver dans une situation où je possède la SuperSuperClass et la ChildClass mais pas la SuperClass, j'ai donc trouvé la première solution utile.
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);

     }
}

courir: UNE CONSTRUCTION RÉUSSIE (durée totale: 0 seconde)

Boris
la source
1
Je vois, mais vous créez une nouvelle instance afin que cela ne fonctionne pas si l'objet a un état.
Tim Büthe
1

L'appel de super.super.method () est logique lorsque vous ne pouvez pas modifier le code de la classe de base. Cela se produit souvent lorsque vous étendez une bibliothèque existante.

Demandez-vous d'abord, pourquoi prolongez-vous cette classe? Si la réponse est "parce que je ne peux pas le changer", vous pouvez créer le package et la classe exacts dans votre application, et réécrire la méthode coquine ou créer un délégué:

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

Par exemple, vous pouvez créer la classe org.springframework.test.context.junit4.SpringJUnit4ClassRunner dans votre application de sorte que cette classe doit être chargée avant la vraie depuis jar. Réécrivez ensuite les méthodes ou les constructeurs.

Attention: Il s'agit d'un hack absolu, et il est fortement déconseillé d'utiliser, mais cela fonctionne! L'utilisation de cette approche est dangereuse en raison de problèmes possibles avec les chargeurs de classe. Cela peut également entraîner des problèmes à chaque fois que vous mettez à jour la bibliothèque qui contient une classe remplacée.

ruruskyi
la source
1

J'ai eu des situations comme celles-ci lorsque l'architecture consiste à créer des fonctionnalités communes dans une CustomBaseClass commune qui implémente au nom de plusieurs classes dérivées. Cependant, nous devons contourner la logique commune pour une méthode spécifique pour une classe dérivée spécifique. Dans de tels cas, nous devons utiliser une implémentation super.super.methodX.

Nous y parvenons en introduisant un membre booléen dans la CustomBaseClass, qui peut être utilisé pour différer sélectivement l'implémentation personnalisée et céder le pas à l'implémentation du framework par défaut lorsque cela est souhaitable.

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

Cependant, avec de bons principes d'architecture suivis dans le cadre ainsi que dans l'application, nous pourrions éviter de telles situations facilement, en utilisant une approche hasA, au lieu d'une approche isA. Mais à tout moment, il n'est pas très pratique de s'attendre à une architecture bien conçue en place, et donc la nécessité de s'éloigner des principes de conception solides et d'introduire des hacks comme celui-ci. Juste mes 2 cents ...

Ganesh Iyer
la source
1

@ Jon Skeet Belle explication. IMO si quelqu'un veut appeler la méthode super.super, alors il faut vouloir ignorer le comportement du parent immédiat, mais vouloir accéder au comportement du grand parent. Ceci peut être réalisé via l'instance de. Comme ci-dessous le 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();
    }
}

Voici la classe de pilote,

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

La sortie de ceci sera

In C Class
In A Class

Le comportement de classe B printClass sera ignoré dans ce cas. Je ne suis pas sûr que ce soit une pratique idéale ou bonne pour atteindre super.super, mais cela fonctionne toujours.

Sanjay Jain
la source
1
Eh bien, c'est créatif mais ne répond pas vraiment à ma question. C n'appelle toujours pas super.super, B se comporte simplement différemment. Si vous pouvez modifier A et B, vous pouvez simplement ajouter une autre méthode au lieu d'utiliser instanceof. Super.super.foo vous aiderait dans les cas où vous n'avez pas accès à A et B et ne pouvez pas les changer.
Tim Büthe
D'accord @TimButhe, mais si quelqu'un veut appeler à super.super alors il / elle veut intentionnellement ignorer le comportement de la classe parente, donc il vous suffit de réaliser cette chose par la syntaxe existante de java. (N'importe quelle option que vous voulez soit instanceof / méthode différente)
Sanjay Jain
0

Si vous pensez que vous allez avoir besoin de la superclasse, vous pouvez la référencer dans une variable pour cette classe. Par exemple:

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

Devrait imprimer:

2
1
0
Ashtheking
la source
2
Votre exmaple est en quelque sorte ... bien mauvais, car vous utilisez des méthodes statiques. Lorsque vous utilisez des méthodes statiques, vous n'avez pas du tout besoin de la variable ou de super. Peut-être que vous avez manqué un concept OO de base ici, assurez-vous de le lire. Dois voter, désolé.
Tim Büthe
1
Cela aurait pu facilement être fait sans méthodes statiques. C'est assez simple. J'allais pour un exemple simple sur la façon de le faire.
Ashtheking
Je comprends votre point de vue, le stockage de la super variable dans un champ est un moyen de résoudre ce problème. Mais, vous n'avez pas besoin de la variable s'il s'agit d'une méthode statique, vous pouvez simplement l'utiliser. Deuxièmement, invoquer des méthodes statiques une variable est une mauvaise pratique et la plupart des IDE vous en avertiront. Si vous corrigez votre réponse, en supprimant les éléments statiques, je serais heureux de supprimer mon downvote, sans offense.
Tim Büthe
Vous êtes proche, mais votre code ne se compilera pas. Vous essayez d'accéder à getNumber à partir d'un contexte statique dans votre méthode principale. Avez-vous réellement essayé de compiler cela? (et votre UltraFoo ne devrait-il pas étendre SuperFoo?)
Tim Büthe
Je ne veux vraiment pas être cruel avec vous, mais new UltraFoo.getNumber()je ne compilerai pas, car vous avez manqué les parenthèses là-bas. Cependant, je viens de supprimer mon don, car le concept de votre code est assez clair maintenant, merci!
Tim Büthe
0

IMO, c'est une façon propre de réaliser un super.super.sayYourName()comportement en 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);
    }
}

Production:

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

Yakov Fain
la source
ah, donc vous préconisez la mise en œuvre d'une hiérarchie de classes comme celle-ci? Malheureusement, cela commence à devenir vraiment compliqué si vous voulez accéder à la méthode dans Mama from Baby (sous-classe de fille) ...
bcr
Yakov Fain a raison. en d'autres termes, ce n'est pas un bon exemple, car la question d'origine était d'appeler une méthode surchargée dans super.super.
2012 à 9h17
0

Je pense que c'est un problème qui rompt l'accord de succession.
En étendant une classe, vous obéissez / acceptez son comportement, caractéristiques
Pendant que vous appelez super.super.method(), vous voulez rompre votre propre accord d'obéissance.

Vous ne pouvez tout simplement pas choisir la super classe .

Cependant, il peut arriver des situations où vous ressentez le besoin d'appeler super.super.method()- généralement un mauvais signe de conception, dans votre code ou dans le code dont vous héritez!
Si les classes super et super super ne peuvent pas être refactorisées (certains codes hérités), optez pour la composition plutôt que l'héritage.

La rupture d'encapsulation consiste à @Override certaines méthodes en cassant le code encapsulé. Les méthodes conçues pour ne pas être remplacées sont marquées comme finales .

DayaMoon
la source
0

En C #, vous pouvez appeler une méthode de n'importe quel ancêtre comme ceci:

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

Vous pouvez également le faire dans Delphi:

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

Mais en Java, vous ne pouvez faire cette mise au point que par un certain équipement. Une façon possible est:

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

Sortie du résultat 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
D.Motyl
la source
En C #, cela ne fonctionnera que pour les méthodes non virtuelles et comme toutes les méthodes sont virtuelles en Java, ce n'est pas vraiment différent.
Agent_L
0

C'est tout simplement facile à faire. Par exemple:

Sous-classe C de B et sous-classe B de A. Les deux ont par exemple la méthode 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");
    }

}

Exécuter la classe C La sortie sera: Classe A Classe C

Au lieu de la sortie: Classe A Classe B Classe C

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

Sortie: imprimée sur le grand-père

Andrey
la source
2
Cette réponse sort du cadre de la question. L'OP n'a pas demandé comment appeler une méthode dans une classe de grands-parents. Le problème est de savoir pourquoi le super.super.method()code n'est pas valide en Java.
Jed Schaaf
-1

Le mot-clé super n'est qu'un moyen d'invoquer la méthode dans la superclasse. Dans le tutoriel Java: https://docs.oracle.com/javase/tutorial/java/IandI/super.html

Si votre méthode remplace l'une des méthodes de sa superclasse, vous pouvez invoquer la méthode substituée en utilisant le mot clé super.

Ne croyez pas que c'est une référence du super objet !!! Non, c'est juste un mot-clé pour invoquer des méthodes dans la superclasse.

Voici un exemple:

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

Lorsque vous appelez cat.doSth(), la méthode doSth()en classe Animals'imprime thiset c'est un chat.

qing li
la source