web-dev-qa-db-fra.com

Java: appel d'une super méthode qui appelle une méthode remplacé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();
    }
}

ma sortie attendue:

sous-classe method1
méthode superclasse1
méthode superclasse2 

rendement réel:

sous-classe method1
méthode superclasse1
sous-classe method2

Je sais que techniquement j'ai annulé une méthode publique, mais je me suis dit que parce que j'appelais le super, tous les appels dans le super resteraient dans le super, cela ne se produirait pas. Des idées sur la façon dont je peux y arriver?

76
jsonfry

Le mot clé super ne "colle" pas. Chaque appel de méthode est traité individuellement. Ainsi, même si vous devez appeler SuperClass.method1() en appelant super, cela n’influencera aucun autre appel de méthode que vous pourriez effectuer ultérieurement.

Cela signifie qu'il n'y a pas de moyen direct d'appeler SuperClass.method2() à partir de SuperClass.method1() sans passer par SubClass.method2(), sauf si vous travaillez avec une instance réelle de SuperClass.

Vous ne pouvez même pas obtenir l'effet souhaité en utilisant Reflection (voir la documentation de Java.lang.reflect.Method.invoke(Object, Object...) ).

[EDIT] Il semble y avoir encore une certaine confusion. Laissez-moi essayer une explication différente.

Lorsque vous appelez foo(), vous appelez en fait this.foo(). Java vous permet simplement d’omettre la variable this. Dans l'exemple de la question, le type de this est SubClass.

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

L'utilisation de super ne modifie pas l'instance désignée par this. Donc, l'appel va à SubClass.method2() puisque this est de type SubClass.

Il est peut-être plus facile de comprendre lorsque vous imaginez que Java passe this en tant que premier paramètre masqué:

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 constaterez que this ne change jamais, il s'agit toujours de l'instance créée dans main().

65
Aaron Digulla

Vous ne pouvez accéder aux méthodes remplacées que par celles-ci (ou par d'autres méthodes de la classe substituée).

Donc: ne remplacez pas method2() ou appelez super.method2() dans la version remplacée.

12
Sean Patrick Floyd

Vous utilisez le mot clé this qui fait en réalité référence à "l'instance en cours d'exécution de l'objet que vous utilisez", c'est-à-dire que vous appelez this.method2(); sur votre super-classe, c'est-à-dire qu'il appelle la méthode method2 () sur l'objet. vous utilisez, qui est la sous-classe.

8
Jose Diaz

Comme le seul moyen d'éviter une méthode à annuler est d'utiliser le mot clé super , j'ai pensé déplacer la méthode method2 () de SuperClass vers une autre nouvelle classe Base, puis appelez-le depuis SuperClass:

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

class SuperClass extends Base
{
    public void method1()
    {
        System.out.println("superclass method1");
        super.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");
    }
}

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

Sortie:

subclass method1
superclass method1
superclass method2
2
carlos_lm
    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");
    }
}



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

Sortie:

sous-classe method1
superclass method1
superclass method2

2
Vijay

Si vous ne souhaitez pas que superClass.method1 appelle subClass.method2, rendez method2 privé afin d'éviter toute substitution.

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 cela ne fonctionnait pas de cette façon, le polymorphisme serait impossible (ou du moins pas même utile).

2
Joeri Hendrickx

this fait toujours référence à l'objet en cours d'exécution.

Pour illustrer davantage ce point, voici un croquis simple:

+----------------+
|  Subclass      |
|----------------|
|  @method1()    |
|  @method2()    |
|                |
| +------------+ |
| | Superclass | |
| |------------| |
| | method1()  | |
| | method2()  | |
| +------------+ |
+----------------+

Si vous avez une instance de la boîte externe, un objet Subclass, où que vous vous aventuriez à l'intérieur de la boîte, même dans la Superclass 'zone', il s'agit toujours de l'instance de la boîte externe.

De plus, dans ce programme, il n’ya qu’un seul objet créé à partir des trois classes, donc this ne peut jamais faire référence à une chose et c’est:

 enter image description here

comme indiqué dans le Netbeans 'Heap Walker'.

2
Johnny Baloney

Pour résumer, ceci pointe sur l'objet actuel et l'invocation de méthode en Java est par nature polymorphe. Ainsi, la sélection de la méthode pour l'exécution dépend totalement de l'objet pointé par celle-ci. Par conséquent, l'invocation de la méthode method2 () à partir de la classe parent appelle method2 () de la classe enfant, car cela pointe sur l'objet de la classe enfant. La définition de cela ne change pas, quelle que soit la classe utilisée.

PS. contrairement aux méthodes, les variables membres de la classe ne sont pas polymorphes.

1

Le mot-clé "this" fait référence à la référence de classe actuelle. Cela signifie que, quand elle est utilisée dans la méthode, la classe 'actuelle' est toujours SubClass et la réponse est donc expliquée.

1
Ozil

J'y pense de cette façon

+----------------+
|     super      |
+----------------+ <-----------------+
| +------------+ |                   |
| |    this    | | <-+               |
| +------------+ |   |               |
| | @method1() | |   |               |
| | @method2() | |   |               |
| +------------+ |   |               |
|    method4()   |   |               |
|    method5()   |   |               |
+----------------+   |               |
    We instantiate that class, not that one!

Permettez-moi de déplacer cette sous-classe un peu à gauche pour révéler ce qui se trouve en dessous de ... (Homme, j'aime les graphiques ASCII)

We are here
        |
       /  +----------------+
      |   |     super      |
      v   +----------------+
+------------+             |
|    this    |             |
+------------+             |
| @method1() | method1()   |
| @method2() | method2()   |
+------------+ method3()   |
          |    method4()   |
          |    method5()   |
          +----------------+

Then we call the method
over here...
      |               +----------------+
 _____/               |     super      |
/                     +----------------+
|   +------------+    |    bar()       |
|   |    this    |    |    foo()       |
|   +------------+    |    method0()   |
+-> | @method1() |--->|    method1()   | <------------------------------+
    | @method2() | ^  |    method2()   |                                |
    +------------+ |  |    method3()   |                                |
                   |  |    method4()   |                                |
                   |  |    method5()   |                                |
                   |  +----------------+                                |
                   \______________________________________              |
                                                          \             |
                                                          |             |
...which calls super, thus calling the super's method1() here, so that that
method (the overidden one) is executed instead[of the overriding one].

Keep in mind that, in the inheritance hierarchy, since the instantiated
class is the sub one, for methods called via super.something() everything
is the same except for one thing (two, actually): "this" means "the only
this we have" (a pointer to the class we have instantiated, the
subclass), even when Java syntax allows us to omit "this" (most of the
time); "super", though, is polymorphism-aware and always refers to the
superclass of the class (instantiated or not) that we're actually
executing code from ("this" is about objects [and can't be used in a
static context], super is about classes).

En d'autres termes, citant la Spécification du langage Java :

Le formulaire super.Identifier fait référence au champ nommé Identifier du fichier objet en cours, mais avec l'objet en cours considéré comme une instance de la superclasse de la classe actuelle.

Le formulaire T.super.Identifier fait référence au champ nommé Identifier de l'instance englobant lexicalement correspondant à T, mais avec celle instance considérée comme une instance de la super-classe de T.

En termes simples, this est fondamentalement un objet (* l'objet **; le même objet que vous pouvez déplacer dans des variables), l'instance de la classe instanciée, une variable simple dans le domaine de données; super est comme un pointeur sur un bloc de code emprunté que vous voulez exécuter, plutôt comme un simple appel de fonction, et il est relatif à la classe où il est appelé.

Par conséquent, si vous utilisez super de la superclasse, vous obtenez le code de la classe superduper [le grand-parent] exécuté), tandis que si vous utilisez this (ou s’il est utilisé de manière implicite) à partir d’une superclasse, il pointe toujours vers la sous-classe (car personne ne l’a modifié). et personne ne pouvait).

1
Unai Vivi

Je ne crois pas que vous puissiez le faire directement. Une solution de contournement serait d'avoir une implémentation interne privée de method2 dans la superclasse et d'appeler cela. Par exemple:

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

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

}
1
David Gelhar

Au cours de mes recherches pour un cas similaire, je me suis retrouvé à vérifier la trace de la pile dans la méthode de la sous-classe pour savoir d'où venait l'appel. Il y a probablement des moyens plus intelligents de le faire, mais cela fonctionne pour moi et c'est une approche dynamique.

public void method2(){
        Exception ex=new Exception();
        StackTraceElement[] ste=ex.getStackTrace();
        if(ste[1].getClassName().equals(this.getClass().getSuperclass().getName())){
            super.method2();
        }
        else{
            //subclass method2 code
        }
}

Je pense que la question d'avoir une solution pour le cas est raisonnable. Il existe bien sûr des moyens de résoudre le problème avec différents noms de méthodes ou même avec différents types de paramètres, comme déjà mentionné dans le fil de discussion, mais dans mon cas, je n'aime pas trop confondre les noms de méthodes.

0
Beat Siegrist