112 votes

Java : Appel d'une super méthode qui appelle une méthode surchargée

public class SuperClass
{
    public void method1()
    {
        System.out.println("superclass method1");
        this.method2();
    }

    public void method2()
    {
        System.out.println("superclass method2");
    }

}

public class SubClass extends SuperClass
{
    @Override
    public void method1()
    {
        System.out.println("subclass method1");
        super.method1();
    }

    @Override
    public void method2()
    {
        System.out.println("subclass method2");
    }
}

public class Demo 
{
    public static void main(String[] args) 
    {
        SubClass mSubClass = new SubClass();
        mSubClass.method1();
    }
}

mon résultat attendu :

sous-classe méthode1
superclasse méthode1
superclasse méthode2

la production réelle :

sous-classe méthode1
superclasse méthode1
sous-classe méthode2

Je sais que techniquement j'ai surchargé une méthode publique, mais je pensais que parce que j'appelais le super, tous les appels à l'intérieur du super resteraient dans le super, ce qui n'est pas le cas. Comment faire pour qu'il en soit ainsi ?

2 votes

Je soupçonne que vous voudriez "préférer la composition à l'héritage".

98voto

Aaron Digulla Points 143830

Le mot-clé super ne "colle" pas. Chaque appel de méthode est traité individuellement, de sorte que même si vous êtes arrivé à SuperClass.method1() en appelant super qui n'influence pas les autres appels de méthode que vous pourriez faire à l'avenir.

Cela signifie qu'il n'y a pas de moyen direct d'appeler SuperClass.method2() de SuperClass.method1() sans passer par SubClass.method2() à moins que vous ne travailliez avec une instance réelle de SuperClass .

Il n'est même pas possible d'obtenir l'effet désiré en utilisant la Réflexion (voir la documentation de java.lang.reflect.Method.invoke(Object, Object...) ).

[EDIT] Il semble qu'il y ait encore une certaine confusion. Permettez-moi d'essayer une autre explication.

Lorsque vous invoquez foo() vous invoquez en fait this.foo() . Java vous permet simplement d'omettre l'élément this . Dans l'exemple de la question, le type de this es SubClass .

Ainsi, lorsque Java exécute le code dans SuperClass.method1() il arrive finalement à this.method2();

Utilisation super ne modifie pas l'instance pointée par this . L'appel est donc adressé à SubClass.method2() desde this est du type SubClass .

C'est peut-être plus facile à comprendre si l'on imagine que Java passe this comme premier paramètre caché :

public class SuperClass
{
    public void method1(SuperClass this)
    {
        System.out.println("superclass method1");
        this.method2(this); // <--- this == mSubClass
    }

    public void method2(SuperClass this)
    {
        System.out.println("superclass method2");
    }

}

public class SubClass extends SuperClass
{
    @Override
    public void method1(SubClass this)
    {
        System.out.println("subclass method1");
        super.method1(this);
    }

    @Override
    public void method2(SubClass this)
    {
        System.out.println("subclass method2");
    }
}

public class Demo 
{
    public static void main(String[] args) 
    {
        SubClass mSubClass = new SubClass();
        mSubClass.method1(mSubClass);
    }
}

Si vous suivez la pile d'appels, vous pouvez voir que this ne change jamais, il s'agit toujours de l'instance créée dans main() .

0 votes

Quelqu'un pourrait-il télécharger un diagramme de ce (jeu de mots) qui passe par la pile ? merci d'avance !

2 votes

@laycat : Il n'y a pas besoin de diagramme. Rappelez-vous simplement que Java n'a pas de "mémoire" pour les super . Chaque fois qu'il appellera une méthode, il regardera le type d'instance et commencera à chercher la méthode avec ce type, peu importe le nombre de fois que vous avez appelé super . Ainsi, lorsque vous appelez method2 sur une instance de SubClass il verra toujours celui de SubClass d'abord.

0 votes

@AaronDigulla, Pouvez-vous nous en dire plus sur le fait que "Java n'a pas de mémoire pour les super" ?

17voto

Sean Patrick Floyd Points 109428

Vous ne pouvez accéder aux méthodes surchargées que dans les méthodes surchargées (ou dans d'autres méthodes de la classe surchargée).

Donc : soit vous ne passez pas outre method2() ou appeler super.method2() à l'intérieur de la version remplacée.

8voto

StudiousJoseph Points 3578

Vous utilisez le this qui se réfère en fait à "l'instance en cours d'exécution de l'objet que vous utilisez", c'est-à-dire que vous invoquez le mot-clé this.method2(); sur votre superclasse, c'est-à-dire qu'il appellera la méthode2() sur l'objet que vous utilisez, qui est la sous-classe.

9 votes

Vrai, et ne pas utiliser this n'aidera pas non plus. Une invocation non qualifiée utilise implicitement this

3 votes

Pourquoi cette question est-elle votée en haut lieu ? Ce n'est pas la réponse à cette question. Lorsque vous écrivez method2() le compilateur verra this.method2() . Ainsi, même si vous supprimez le this il ne fonctionne toujours pas. Ce que dit @Sean Patrick Floyd est correct

4 votes

@Shervin il ne dit rien de mal, il n'explique pas clairement ce qui se passe si on laisse de côté this

3voto

Joeri Hendrickx Points 6957

Si vous ne voulez pas que superClass.method1 appelle subClass.method2, rendez method2 private pour qu'elle ne puisse pas être surchargée.

Voici une suggestion :

public class SuperClass {

  public void method1() {
    System.out.println("superclass method1");
    this.internalMethod2();
  }

  public void method2()  {
    // this method can be overridden.  
    // It can still be invoked by a childclass using super
    internalMethod2();
  }

  private void internalMethod2()  {
    // this one cannot.  Call this one if you want to be sure to use
    // this implementation.
    System.out.println("superclass method2");
  }

}

public class SubClass extends SuperClass {

  @Override
  public void method1() {
    System.out.println("subclass method1");
    super.method1();
  }

  @Override
  public void method2() {
    System.out.println("subclass method2");
  }
}

Si ce n'était pas le cas, le polymorphisme serait impossible (ou du moins pas aussi utile).

3voto

Vijay Points 11
class SuperClass
{
    public void method1()
    {
        System.out.println("superclass method1");
        SuperClass se=new SuperClass();
        se.method2();
    }

    public void method2()
    {
        System.out.println("superclass method2");
    }
}

class SubClass extends SuperClass
{
    @Override
    public void method1()
    {
        System.out.println("subclass method1");
        super.method1();
    }

    @Override
    public void method2()
    {
        System.out.println("subclass method2");
    }
}

appel

SubClass mSubClass = new SubClass();
mSubClass.method1();

sorties

sous-classe méthode1
superclasse méthode1
superclasse méthode2

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X