36 votes

La référence est ambiguë avec les génériques

Je suis tout à fait avoir un cas délicat ici avec les médicaments génériques et de la surcharge de méthode. Découvrez cette classe d'exemple:

public class Test {
    public <T> void setValue(Parameter<T> parameter, T value) {
    }

    public <T> void setValue(Parameter<T> parameter, Field<T> value) {
    }

    public void test() {
        // This works perfectly. <T> is bound to String
        // ambiguity between setValue(.., String) and setValue(.., Field)
        // is impossible as String and Field are incompatible
        Parameter<String> p1 = getP1();
        Field<String> f1 = getF1();
        setValue(p1, f1);

        // This causes issues. <T> is bound to Object
        // ambiguity between setValue(.., Object) and setValue(.., Field)
        // is possible as Object and Field are compatible
        Parameter<Object> p2 = getP2();
        Field<Object> f2 = getF2();
        setValue(p2, f2);
    }

    private Parameter<String> getP1() {...}
    private Parameter<Object> getP2() {...}

    private Field<String> getF1() {...}
    private Field<Object> getF2() {...}
}

L'exemple ci-dessus compile parfaitement dans Eclipse (Java 1.6), mais pas avec la Fourmi commande javac (ou avec le JDK de commande de javac), où je reçois ce type de message d'erreur sur le deuxième appel d' setValue:

référence à setValue est ambigu, les deux méthode setValue(org.jooq.Paramètre,T) dans l'Essai et la méthode setValue(org.jooq.Paramètre,org.jooq.Champ) en Test match

Selon le cahier des charges et à ma compréhension de la façon dont le compilateur Java fonctionne, le plus spécifique de la méthode doit toujours être choisi: http://java.sun.com/docs/books/jls/third_edition/html/expressions.html#20448

En tout cas, même si <T> est lié à l' Object, ce qui le rend à la fois setValue méthodes acceptables candidats pour l'invocation, l'un avec l' Field paramètre semble toujours être plus précis. Et cela fonctionne dans Eclipse, mais pas avec le JDK du compilateur.

Mise à JOUR:

Comme cela, il serait de travailler à la fois dans Eclipse et avec le JDK compilateur (avec rawtypes mises en garde, bien sûr). Je comprends que les règles mentionnées dans les spécifications sont assez spéciales, lorsque les génériques sont impliqués. Mais je trouve cela un peu confus:

    public <T> void setValue(Parameter<T> parameter, Object value) {
    }

    // Here, it's easy to see that this method is more specific
    public <T> void setValue(Parameter<T> parameter, Field value) {
    }

Mise à JOUR 2:

Même avec les génériques, je peux créer cette solution de contournement, où j'ai éviter le type <T> liés à l' Object à setValue invocation du temps, en ajoutant des supplémentaires de, sans ambiguïté indirection appelés setValue0. Cela me fait penser que la liaison de l' T de Object c'est vraiment ce qui est la cause de tous les ennuis ici:

    public <T> void setValue(Parameter<T> parameter, T value) {
    }

    public <T> void setValue(Parameter<T> parameter, Field<T> value) {
    }

    public <T> void setValue0(Parameter<T> parameter, Field<T> value) {
        // Quite obviously, this call is not ambiguous
        setValue(parameter, value);
    }

    public void test() {
        Parameter<Object> p2 = p2();
        Field<Object> f2 = f2();
        setValue0(p2, f2);
    }

Suis-je un malentendu quelque chose ici? Est-il connu du compilateur bug lié à cela? Ou est-il une solution/compilateur paramètre pour m'aider?

Suivi:

Pour ceux que cela intéresse, j'ai déposé un rapport de bug à la fois à Oracle et Eclipse. Oracle a accepté le bug, jusqu'à présent, Eclipse a analysé et l'a rejeté! Il semble que si mon intuition est bonne et c'est un bug en javac

24voto

irreputable Points 25577

JDK est droit. La 2e méthode n'est pas plus précis que le 1er. De JLS3#15.12.2.5

"Le secteur informel de l'intuition est qu'une méthode est plus spécifique que l'autre, si toute invocation gérées par la première méthode peut être transmise à l'autre sans un moment de la compilation type d'erreur."

Ce n'est clairement pas le cas ici. Je l'ai souligné tout l'invocation. La propriété d'une méthode plus spécifique que l'autre purement repose sur les deux méthodes elles-mêmes; elle ne change pas par invocation.

Analyse formelle sur votre problème: est-m2 plus spécifique que la m1?

m1: <R> void setValue(Parameter<R> parameter, R value) 
m2: <V> void setValue(Parameter<V> parameter, Field<V> value) 

Tout d'abord, le compilateur doit en déduire R à partir de la première contraintes:

Parameter<V>   <<   Parameter<R>
Field<V>       <<   R

Le résultat est R=V, par règles d'inférence dans 15.12.2.7

Maintenant, on substitue R et de vérifier les relations de sous-type

Parameter<V>   <:   Parameter<V>
Field<V>       <:   V

La 2e ligne ne tient pas, par sous-typage des règles en 4.10.2. Afin de m2 n'est pas plus spécifique que la m1.

V n'est Object dans cette analyse, l'analyse considère toutes les valeurs possibles de l' V.

Je suggère d'utiliser différents noms de méthode. La surcharge n'est jamais une nécessité.


Cela semble être un important bug dans Eclipse. La spec assez indique clairement que le type de variables ne sont pas remplacés dans cette étape. Eclipse ne semble variable de type substitution d'abord, puis la méthode de contrôle de la spécificité de la relation.

Si un tel comportement est plus "sensible" dans certains exemples, il n'est pas dans d'autres exemples. Dire,

m1: <T extends Object> void check(List<T> list, T obj) { print("1"); }
m2: <T extends Number> void check(List<T> list, T num) { print("2"); }

void test()
    check( new ArrayList<Integer>(), new Integer(0) );

"Intuitivement", et officiellement par spec, le m2 est plus spécifique que la m1, et le test s'imprime "2". Toutefois, si la substitution T=Integer est faite en premier, les deux méthodes ne sont pas identiques!


pour la mise à Jour 2

m1: <R> void setValue(Parameter<R> parameter, R value) 
m2: <V> void setValue(Parameter<V> parameter, Field<V> value) 

m3: <T> void setValue2(Parameter<T> parameter, Field<T> value)
s4:             setValue(parameter, value)

Ici, m1 n'est pas applicable pour l'invocation de méthode s4, de sorte m2 est le seul choix.

Par 15.12.2.2, pour voir si m1 est applicable pour les s4, tout d'abord, l'inférence de type est effectué, à la conclusion que R=T; puis nous vérifions Ai :< Si, ce qui conduit à l' Field<T> <: T, ce qui est faux.

Ceci est cohérent avec l'analyse précédente - si m1 est applicable à s4, toute invocation traitées par m2 (essentiellement les mêmes que s4) peuvent être traitées par m1, ce qui signifie m2 serait plus précis que le m1, ce qui est faux.

dans un type paramétré

Considérons le code suivant

class PF<T>
{
    public void setValue(Parameter<T> parameter, T value) {
    }

    public void setValue(Parameter<T> parameter, Field<T> value) {
    }
}

void test()

    PF<Object> pf2 = null;

    Parameter<Object> p2 = getP2();
    Field<Object> f2 = getF2();

    pf2.setValue(p2,f2);

Cette compile sans problème. Par 4.5.2, les types de méthodes en PF<Object> sont des méthodes en PF<T> de substitution) T=Object. Qui est, les méthodes de pf2 sont

    public void setValue(Parameter<Object> parameter, Object value) 

    public void setValue(Parameter<Object> parameter, Field<Object> value) 

La 2ème méthode est plus spécifique que le 1er.

0voto

Buhake Sindi Points 38654

Ma conjecture est que le compilateur fait une surcharge de la méthode de résolution par JLS, Section 15.12.2.5.

Pour cette Section, le compilateur utilise une forte sous-typage (ne permettant pas à tout unchecked conversion), de sorte que, T value devient Object value et Field<T> value devient Field<Object> value. Les règles suivantes s'appliquent:

La méthode m est applicable par le sous-typage si et seulement si les deux conditions suivantes s'appliquent:

* For 1in, either:
      o Ai is a subtype (§4.10) of Si (Ai <: Si) or
      o Ai is convertible to some type *Ci* by unchecked conversion

(§5.1.9), et Ci <: Es. * Si m est une méthode générique comme décrit ci-dessus puis Ul <: Bl[R1 = U1, ..., Rp = Up], 1lp.

(Voir point 2). Depuis Field<Object> est un sous-type d' Object alors le plus spécifique de la méthode se trouve. Champ f2 correspond à la fois à des méthodes de la vôtre (à cause du point 2 ci-dessus) et la rend ambiguë.

Pour String et Field<String>, il n'y a pas de sous-type de la relation entre les deux.

PS. C'est ma compréhension des choses, ne pas le citer comme casher.

0voto

Georgy Bolyuba Points 3932

Edit: Cette réponse est fausse. Jetez un oeil à accepté de répondre.

Je pense que le problème vient de ce: compilateur ne pas voir le type de f2 (c'est à dire le Terrain) et le type inféré du paramètre formel (c'est à dire le Terrain> Terrain) que le même type.

En d'autres termes, il ressemble type de f2 (Champ) est considéré comme un sous-type du type de paramètre formel de Champ (Domaine). Depuis le Champ est le même type d'un sous-type de l'Objet, le compilateur ne peut pas choisir une méthode plutôt qu'une autre.

Edit: Permettez-moi de développer ma déclaration un peu

Les deux méthodes sont applicables et il semble que la Phase 1: Identifier Correspondant Arité de Méthodes Applicables par le sous-typage est utilisé pour décider de la méthode à appeler et que les règles de Choisir le Plus Spécifique de la Méthode appliquée, mais a échoué pour une raison de choisir la deuxième méthode de plus pour le premier.

La Phase 1 de l'article utilise cette notation: X <: S (X est le sous-type de S). Basé sur ma connaissance de la <:, X <: X est une expression valide, c'est à dire l' <: n'est pas stricte et comprend le type lui-même (X est le sous-type de X) dans ce contexte. C'est ce qui explique le résultat de la Phase 1: les deux méthodes sont choisis comme candidats, depuis Field<Object> <: Object et Field<Object> <: Field<Object>.

Le choix de la Méthode Spécifique de la section utilise la même notation pour dire qu'une méthode est plus spécifique que l'autre. La partie intéressante du paragraphe qui commence par "Un fixe-arité de la méthode de membre nommé m est plus spécifique que l'autre membre...". Il a, entre autres choses:

Pour tout j de 1 à n, Tj <: Sj.

Cela me fait penser que dans notre cas de la deuxième méthode doit être choisie au-dessus de la première, parce que la suite est titulaire:

  • Parameter<Object> <: Parameter<Object>
  • Field<Object> <: Object

alors que l'inverse ne tient pas en raison d' Object <: Field<Object> d'être faux (l'Objet n'est pas un sous-type de Champ).

Remarque: Dans le cas des exemples de Chaîne, la Phase 1 s'il suffit de choisir la seule méthode applicable: le deuxième.

Donc, pour répondre à tes questions: je pense que c'est un bug dans le compilateur de mise en œuvre. Eclipse a c'est propre différentiels compilateur, ce qui n'a pas ce bug, il me semble.

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