28 votes

Pourquoi Java assure-t-il la compatibilité des types de retour pour les méthodes statiques surchargées ?

Par cette réponse y cette réponse Les méthodes statiques Java ne sont pas virtuelles et ne peuvent pas être remplacées. Intuitivement, cela devrait donc fonctionner (même si, dans 99 % des cas, il s'agit d'une programmation dangereuse) :

class Foo
{
    public static String frob() {
        return "Foo";
    }
}

class Bar extends Foo
{
    public static Number frob() {
        return 123;
    }
}

Toutefois, dans la pratique, cela ne suffit pas :

Foo.java:10: frob() in Bar cannot override frob() in Foo; attempting to use incompatible return type
found   : java.lang.Number
required: java.lang.String
    public static Number frob() {
                         ^

Naïvement, il semble que Foo.frob() y Bar.frob() ne devraient pas avoir de rapport entre eux, mais Java insiste sur le fait qu'ils en ont un. Mais Java insiste sur le fait que c'est le cas. Pourquoi ?

(N.b. : je ne veux pas savoir pourquoi ce serait une mauvaise idée de coder de cette manière, je veux savoir ce qui, dans Java et/ou dans la conception de la JVM, rend cette restriction nécessaire).


Mise à jour pour ajouter : Pour ceux qui pensent que le compilateur va s'embrouiller en appelant des méthodes statiques sur des instances, si vous autorisez cela : il ne le fera pas. Il doit déjà s'en rendre compte dans le cas où les signatures des méthodes sont compatible :

class Foo
{
    static String frob() {
        return "Foo";
    }
}

class Bar extends Foo
{
    static String frob() {
        return "Bar";
    }
}

class Qux {
    public static void main(String[] args) {
        Foo f = new Foo();
        Foo b = new Bar();
        Bar b2 = new Bar();

        System.out.println(f.frob());
        System.out.println(b.frob());
        System.out.println(b2.frob());
    }
}

vous obtient :

Foo
Foo
Bar

La question est de savoir quelle est la raison concrète pour laquelle il ne pourrait pas aussi facilement (dans le cas de signatures incompatibles) vous atteindre :

Foo
Foo
123

8voto

Louis Wasserman Points 67557

Considérez ce qui suit :

public class Foo {
  static class A {
    public static void doThing() {
      System.out.println("the thing");
    }
  }

  static class B extends A {

  }

  static class C extends B {
    public static void doThing() {
      System.out.println("other thing");
    }
  }

  public static void main(String[] args) {
    A.doThing();
    B.doThing();
    C.doThing();
  }
}

Exécutez-la ! Il compile et imprime

the thing
the thing
other thing

Les méthodes statiques héritent en quelque sorte - dans le sens où B.doThing est traduit en un appel à A.doThing -- et peuvent en quelque sorte être remplacés.

Il semble que ce soit surtout une question de jugement de la part de la JLS. La manière la plus spécifique dont la JLS semble aborder cette question est la suivante section 8.2 qui ne dit tout simplement pas que les méthodes statiques ne sont pas hérité.

3voto

Dave Newton Points 93112

JLS 8.4.2 Signature de la méthode , brièvement :

Deux méthodes ont la même signature si elles ont le même nom et les mêmes types d'arguments.

Rien n'est dit sur la statique. Les méthodes statiques peuvent être appelées par l'intermédiaire d'une instance (ou d'une référence nulle) - comment la méthode doit-elle être résolue si une sous-classe est référencée par une déclaration de superclasse ?

2voto

scibuff Points 5796

En effet, en Java, une méthode particulière est appelée en fonction du type de l'objet au moment de l'exécution et non au moment de la compilation. Cependant, les méthodes statiques sont des méthodes de classe et, par conséquent, leur accès est toujours résolu au moment de la compilation en utilisant uniquement les informations sur le type au moment de la compilation. En d'autres termes, que se passerait-il si vous compiliez le code ci-dessus et utilisiez le code suivant

Foo bar = new Bar();
bar.frob();

1voto

Ryan Shillington Points 558

La JVM pourrait probablement le permettre, mais nous allons voir pourquoi c'est une mauvaise idée du point de vue du compilateur.

Les données de l'instance (y compris le type de l'instance) ne sont pas un problème simple à résoudre au moment de la compilation. Il est évident qu'elles sont bien connues au moment de l'exécution. Si j'ai une variable bar de type Bar, et que j'appelle s = bar.frob(), le compilateur devra faire de l'ingénierie inverse pour déterminer le type de bar et voir si la valeur de retour est acceptable. Si la détermination du type au moment de la compilation est un problème très difficile, cela rend le compilateur au mieux inefficace. Au pire, la réponse est erronée et vous obtenez des erreurs d'exécution qui auraient dû être détectées à la compilation.

0voto

Sleiman Jneidi Points 2880

Tout d'abord, vous devriez vous demander ce qu'est la surcharge d'une fonction. La surcharge consiste à appeler une fonction de manière polymorphe. par exemple :

class Foo{
  void fn(){...}
} 
class Bar extends Foo{
  void fn(){...}
}
main(){
Foo f=new Bar();
f.fn();
}

donc en fonction de l'objet créé ou assigné à la fn de référence sera appelée, ce qui n'est pas le cas avec une fonction statique parce que les fonctions statiques sont appelées par la classe et non par la par la référence

Foo.fn();

il n'y a donc pas de liaison dynamique ni de polymorphisme.

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