web-dev-qa-db-fra.com

Java 8: la référence à [méthode] est ambiguë

Est-ce que quelqu'un comprend pourquoi le code suivant se compile correctement en Java 7 et inférieur, mais échoue avec Java 8.

public static void main(String[] args) throws Exception {
    put(get("hello"));
}

public static <R> R get(String d) {
    return (R)d;
}

public static void put(Object o) {
    System.err.println("Object " + o);
}

public static void put(CharSequence c) {
    System.err.println("CharSequence " + c);
}

public static void put(char[] c) {
    System.err.println("char[] " + c);
}

La méthode get a un type de retour générique. Dans JDK 7 et ci-dessous, cela se compile bien et la méthode put avec le paramètre Object est choisie. Dans JDK 8, cela ne peut pas être compilé, indiquant que la méthode put est ambiguë.

Apparemment, JDK 8 saute la méthode Object-parameter et recherche les deux dernières méthodes sub-Object-parameter et s'en plaint (c'est-à-dire que si vous ajoutez une autre méthode put avec un autre type de paramètre, le compilateur basculera et se plaindra du nouveau dernier deux méthodes)

Cela semble être un bug.

26
Ken

Votre problème est un effet secondaire de Inférence de type cible généralisée , une amélioration de Java 8.

Qu'est-ce que l'inférence de type cible

Prenons votre exemple de méthode,

public static <R> R get(String d) {
    return (R)d;
}

Maintenant, dans la méthode ci-dessus, le paramètre générique R ne peut pas être résolu par le compilateur car il n'y a pas de paramètre avec R.

Ils ont donc introduit un concept appelé Target-type Inference, Qui permet au paramètre d'être inféré en fonction du paramètre d'affectation.

Donc, si vous le faites,

 String str = get("something"); // R is inferred as String here
 Number num = get("something"); // R is inferred as Number here

Cela fonctionne bien dans Java 7. Mais ce qui suit ne fonctionne pas ,

put(get("something");
static void Put(String str) {} //put method

Parce que l'inférence de type ne fonctionnait que pour les affectations directes.

S'il n'y a pas d'affectation directe, le type générique a été déduit comme Object.

Ainsi, lorsque vous avez compilé le code avec Java 7, votre méthode put(Object) a été appelée sans aucun problème.

Ce qu'ils ont fait en Java 8

Ils ont amélioré l'inférence de type pour déduire le type à partir des appels de méthode et appels de méthode chaînés

Plus de détails à leur sujet ici et ici

Alors maintenant, vous pouvez appeler directement put(get("something")) et le type générique sera déduit basé sur le paramètre de la méthode put().

Mais comme vous le savez, les méthodes, put(Charsequence) et put(char[]) correspondent aux arguments. Il y a donc une ambiguïté.

Réparer?

Dites simplement au compilateur exactement ce que vous voulez,

put(TestClass.<CharSequence>get("hello")); // This will call the put(CharSequence) method.
30
Codebender

Il semble que ce soit une incompatibilité connue.

Voir la section "Area: Tools/javac" de cet article. Et ce bug .

Synopsis

Le code suivant qui a compilé, avec des avertissements, dans JDK 7 ne sera pas compilé dans JDK 8:

import Java.util.List;

class SampleClass {

    static class Baz<T> {
        public static List<Baz<Object>> sampleMethod(Baz<Object> param) {
            return null;
        }
    }

    private static void bar(Baz arg) {
        Baz element = Baz.sampleMethod(arg).get(0);
    }
} 

La compilation de ce code dans JDK 8 produit l'erreur suivante:

SampleClass.Java:12: error:incompatible types: Object cannot be converted to Baz
    Baz element = Baz.sampleMethod(arg).get(0);

Note: SampleClass.Java uses unchecked or unsafe operations.
Note: Recompile with -Xlint:unchecked for details.
1 error 

Dans cet exemple, un type brut est passé à la méthode sampleMethod (Baz) qui est applicable par sous-typage (voir JLS, Java SE 7 Edition, section 15.12.2.2).

Une conversion non cochée est nécessaire pour que la méthode soit applicable, donc son type de retour est effacé (voir JLS, Java SE 7 Edition, section 15.12.2.6). Dans ce cas, le type de retour de sampleMethod (Baz) est Java.util.List au lieu de Java.util.List> et donc le type de retour de get (int) est Object, qui n'est pas compatible avec Baz.

2
Amila