59 votes

Est-ce valide Java?

Est-ce valable de Java?

import java.util.Arrays;
import java.util.List;

class TestWillThatCompile {

    public static String f(List<String> list) {
        System.out.println("strings");
        return null;
    }

    public static Integer f(List<Integer> list) {
        System.out.println("numbers");
        return null;
    }

    public static void main(String[] args) {
        f(Arrays.asList("asdf"));
        f(Arrays.asList(123));
    }

}
  • Eclipse 3.5 dit oui
  • Eclipse 3.6 dit pas
  • Intellij 9 dit oui
  • Soleil javac 1.6.0_20 dit oui
  • GCJ 4.4.3 dit oui
  • Compilateur GWT dit oui
  • À la foule lors de mon précédent Stackoverflow question dit pas

Mon java théorie de la compréhension de la dit pas!

Il serait intéressant de savoir ce que l'JLS est dire à ce sujet.

29voto

Adam Paynter Points 22056

Il dépend de comment vous voulez appeler ces méthodes. Si vous souhaitez appeler ces méthodes à d'autres le code source de Java, il est considéré comme invalide pour des raisons illustré dans Edwin réponse. C'est une limitation du Langage Java.

Cependant, pas toutes les classes doivent être généré à partir du code source Java (compte de toutes les langues qui utilisent la JVM que leur exécution: JRuby, Jython, etc...). Au niveau du bytecode, la JVM peut lever l'ambiguïté entre les deux méthodes, car le bytecode instructions de spécifier le type de retour qu'ils attendent. Pour exemple, voici une classe écrite en Jasmin que l'on peut appeler l'une de ces méthodes:

.class public CallAmbiguousMethod
.super java/lang/Object

.method public static main([Ljava/lang/String;)V
  .limit stack 3
  .limit locals 1

  ; Call the method that returns String
  aconst_null
  invokestatic   TestWillThatCompile/f(Ljava/util/List;)Ljava/lang/String;

  ; Call the method that returns Integer
  aconst_null
  invokestatic   TestWillThatCompile/f(Ljava/util/List;)Ljava/lang/Integer;

  return

.end method

J'ai compiler un fichier de classe à l'aide de la commande suivante:

java-jar jasmin.jar CallAmbiguousMethod.j

Et d'appeler à l'aide:

java CallAmbiguousMethod

Voici, la sortie est:

> java CallAmbiguousMethod
les chaînes
les numéros de

Mise à jour

Simon posté un exemple de programme qui appelle ces méthodes:

import java.util.Arrays;
import java.util.List;

class RealyCompilesAndRunsFine {

    public static String f(List<String> list) {
        return list.get(0);
    }

    public static Integer f(List<Integer> list) {
        return list.get(0);
    }

    public static void main(String[] args) {
        final String string = f(Arrays.asList("asdf"));
        final Integer integer = f(Arrays.asList(123));
        System.out.println(string);
        System.out.println(integer);
    }

}

Voici le bytecode Java généré:

>javap -c RealyCompilesAndRunsFine
Compilé à partir de "RealyCompilesAndRunsFine.java"
classe RealyCompilesAndRunsFine s'étend java.lang.Objet{
RealyCompilesAndRunsFine();
Code:
 0: aload_0
 1: invokespecial #1; //Méthode java/lang/Object."":()V
 4: retour

public static java.lang.String f(java.util.Liste);
Code:
 0: aload_0
 1: iconst_0
 2: invokeinterface #2, 2; //InterfaceMethod java/util/Liste.obtenir:(I)Ljava/lang/Object;
 7: checkcast #3; //la classe java/lang/String
 10: areturn

public static java.lang.Entier f(java.util.Liste);
Code:
 0: aload_0
 1: iconst_0
 2: invokeinterface #2, 2; //InterfaceMethod java/util/Liste.obtenir:(I)Ljava/lang/Object;
 7: checkcast #4; //la classe java/lang/Entier
 10: areturn

public static void main(java.lang.String[]);
Code:
 0: iconst_1
 1: anewarray #3; //la classe java/lang/String
 4: dup
 5: iconst_0
 6: pma #5; //la Chaîne asdf
 8: aastore
 9: invokestatic #6; //Méthode java/util/Tableaux.asList:([Ljava/lang/Object;)Ljava/util/Liste;
 12: invokestatic #7; //la Méthode f:(Ljava/util/Liste;)Ljava/lang/String;
 15: astore_1
 16: iconst_1
 17: anewarray #4; //la classe java/lang/Entier
 20: dup
 21: iconst_0
 22: bipush 123
 24: invokestatic #8; //Méthode java/lang/Entier.valueOf:(I)Ljava/lang/Integer;
 27: aastore
 28: invokestatic #6; //Méthode java/util/Tableaux.asList:([Ljava/lang/Object;)Ljava/util/Liste;
 31: invokestatic #9; //la Méthode f:(Ljava/util/Liste;)Ljava/lang/Integer;
 34: astore_2
 35: getstatic #10; //le Champ java/lang/Système.:Ljava/io/PrintStream;
 38: aload_1
 39: invokevirtual #11; //Méthode java/io/PrintStream.println:(Ljava/lang/String;)V
 42: getstatic #10; //le Champ java/lang/Système.:Ljava/io/PrintStream;
 45: aload_2
 46: invokevirtual #12; //Méthode java/io/PrintStream.println:(Ljava/lang/Object;)V
 49: le retour

Il s'avère que le Soleil compilateur génère du bytecode nécessaire pour lever l'ambiguïté sur les méthodes (voir les instructions 12 et 31 dans la dernière méthode).

Mise à jour #2

La Java Language Specification suggère que cela peut, en fait, être valide, le code source Java. Page 449 (§15.12 Invocation de Méthode Expressions), nous voyons ceci:

Il est possible qu'aucune méthode la plus spécifique, parce qu'il y a deux ou plusieurs méthodes qui sont spécifiques au maximum. Dans ce cas:

  • Si tous le maximum de méthodes spécifiques ont override-équivalent (§8.4.2) les signatures, alors:
    • Si vraiment l'un de la au maximum des méthodes spécifiques n'est pas déclarée abstraite, il est le plus spécifique de la méthode.
    • Sinon, si tous le maximum de méthodes spécifiques sont déclarées abstraites, et les signatures de tous le maximum de méthodes spécifiques ont la même l'effacement (§4.6), puis le plus spécifique de la méthode est choisie arbitrairement parmi le sous-ensemble de la au maximum les méthodes spécifiques qui ont les plus spécifiques le type de retour. Cependant, la plupart de la méthode spécifique est considéré comme jeter un vérifié exception si et seulement si cette exception ou son effacement est déclaré dans les lancers de clauses de chacun le maximum de méthodes spécifiques.
  • Sinon, nous pouvons dire que l'invocation de la méthode est ambigu, et une compile-time erreur se produit.

Sauf erreur de ma part, ce comportement ne devrait s'appliquer qu'à des méthodes déclarées comme abstraite, mais...

Mise à jour #3

Grâce à ILMTitan commentaire:

@Adam Paynter: Votre texte en gras ne pas grave, car il n'est qu'un cas lorsque deux méthodes sont remplacer l'équivalent, qui a montré Dan n'était pas le cas. Ainsi, l' facteur déterminant doit être si la JLS prend des types génériques en compte lors de la la détermination de la plupart des méthodes spécifiques. – ILMTitan

13voto

Edwin Buck Points 33097

--- Edité en réponse aux commentaires ci-dessous ---

Ok, donc c'est valable Java, mais il ne devrait pas l'être. La clé, c'est qu'il n'est pas vraiment en s'appuyant sur le type de retour, mais sur le effacé Génériques paramètre.

Ceci ne fonctionnerait pas sur un non méthode statique, et est explicitement interdit à un non méthode statique. En essayant de le faire dans une classe serait un échec dû à l'apport de questions, la première étant qu'un type de classe n'est pas définitive tant que la classe la Classe est.

C'est une incohérence dans un assez régulière de la langue. TI vais aller sur une branche et dire qu'il devrait être illégal, même si, techniquement autorisé. Il n'a pas vraiment d'ajouter quoi que ce soit à la lisibilité de la langue, et il ajoute peu de peu de à la capacité à résoudre significative des problèmes. Le seule problème, il semble à résoudre est de savoir si vous êtes assez familier avec la langue de savoir quand il est de base principes semblent être violés par la langue, des incohérences internes dans le règlement type de l'effacement, les génériques, et les signatures de méthode.

Certainement le code à éviter, car il est trivial à résoudre le même problème dans un certain nombre de manière plus significative, et le seul intérêt est de voir si l'examinateur, le répéteur connaît une poussiéreux, sale coin de la langue spec.

--- Message d'origine suivante ---

Alors que les compilateurs aurait peut-être elle, la réponse est encore non.

L'effacement va tourner à la fois dans la Liste<String> et List<Integer> sans fioritures dans une Liste. Cela signifie que les deux "f" méthodes ont la même signature, mais différents types de retour. Le type de retour ne peut pas être utilisé pour différencier les méthodes, car cela va échouer lorsque vous revenez dans le cadre d'un super-type; comme:

Object o = f(Arrays.asList("asdf"));

Avez-vous essayé de prendre les valeurs renvoyées dans des variables? Peut-être que le compilateur a optimisé à les choses de telle manière qu'il n'est pas de marcher sur le droit a l'erreur de code.

11voto

Peter Schwarz Points 189

Un doute qui n'a pas été posée est: pourquoi est-il déclencher une erreur de compilation dans Eclipse 3.6?

Voici pourquoi: c'est une fonctionnalité.

Dans javac 7, deux méthodes sont considérées comme les doublons (ou un conflit de nom d'erreur) quel que soit leur type de retour.

Ce comportement est désormais plus cohérent avec javac 1.5, qui a enregistré le nom de clash des erreurs sur les méthodes et ignoré type de retour. Seulement 1.6 a été un changement qui comprenait des types de retour lors de la détection de duplication des méthodes.

Nous avons décidé de faire ce changement à travers tous les niveaux de conformité (1.5, 1.6, 1.7) dans la version 3.6 de sorte que les utilisateurs ne seront pas surpris par le changement s'ils compiler votre code à l'aide de javac 7.

5voto

Dave Points 3143

C'est valable vased sur la spécification.

La signature d'une méthode m1 est un subsignature de la signature d'un méthode m2 si

  • m2 a la même signature que m1, ou

  • la signature de l' m1 est le même que l'effacement de la signature de m2.

Donc, ce ne sont pas des subsignatures de l'autre parce que l'effacement de l' List<String> n'est List<Integer> , et vice-versa.

Deux signatures de méthode m1 et m2sont remplacer l'équivalent iff soit m1 est un subsignature d' m2 ou m2 est un subsignature d' m1.

Donc, ces deux ne sont pas substituer l'équivalent (note de l' iff). Et la règle pour la surcharge est:

Si les deux méthodes d'une classe (que d'être classée dans la même classe, ou à la fois héréditaire par une classe ou d'une déclaré et celui hérité) ont la même nom, mais les signatures qui ne sont pas remplacer l'équivalent, puis la méthode de la le nom est dit être surchargé.

Par conséquent, ces deux méthodes sont surchargées et tout devrait fonctionner.

5voto

Don Branson Points 7100

Eh bien, si je comprends point, trois correctement dans la première liste de l'article 8.4.2 de la spécification, il dit que votre f() méthodes ont la même signature:

http://java.sun.com/docs/books/jls/third_edition/html/classes.html#38649

C'est la spécification qui répond vraiment à cette question, et non pas le comportement observé de compilateur X ou IDE X. Tout ce que nous pouvons dire en regardant les outils est de savoir comment l'auteur de l'outil interprété le spec.

Si nous appliquons balle trois, nous obtenons:

...
 public static String f(List<String> liste) {
Système..println("chaînes");
 return null;
}

 public static Entier f(List<String> liste) {
Système..println("nombre");
 return null;
}
...

et les signatures correspondent, donc il y a une collision, et le code ne compile pas.

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