36 votes

Type incompatible avec Arrays.asList ()

Dans l'exemple suivant, si j'ai plusieurs types dans la liste, il compile ok, mais si j'ai un élément, il choisit un autre type qui n'est plus un droit cessible.

// compiles fine
List<Class<? extends Reference>> list = Arrays.asList(SoftReference.class, WeakReference.class);
// but take an element away and it no longer compiles.
List<Class<? extends Reference>> list2 = Arrays.asList(WeakReference.class);
// without giving the specific type desired.
List<Class<? extends Reference>> list3 = Arrays.<Class<? extends Reference>>asList(WeakReference.class);

Je suis sûr qu'il ya une explication logique à cela, mais il m'échappe.

    Error:Error:line (30)error: incompatible types
required: List<Class<? extends Reference>>
found:    List<Class<WeakReference>>

Pourquoi le fait d'avoir deux éléments de la compilation, mais un élément ne fonctionne pas?

BTW: Il est difficile de trouver un exemple simple, si vous essayez

List<Class<? extends List>> list = Arrays.asList(ArrayList.class, LinkedList.class);

    Error:Error:line (28)error: incompatible types
required: List<Class<? extends List>>
found:    List<Class<? extends INT#1>>
where INT#1 is an intersection type:
INT#1 extends AbstractList,Cloneable,Serializable

Cela ne veut pas compiler ou l'autre (il n'a même pas de parse)

List<Class<? extends AbstractList & Cloneable & Serializable>> list = Arrays.asList(ArrayList.class, LinkedList.class);

Error:Error:line (30)error: > expected
Error:Error:line (30)error: ';' expected

mais cette compile fine

static abstract class MyList<T> implements List<T> { }
List<Class<? extends List>> list = 
        Arrays.asList(ArrayList.class, LinkedList.class, MyList.class);
List<Class<? extends List>> list = 
        Arrays.<Class<? extends List>>asList(ArrayList.class, LinkedList.class);

EDIT: Basé sur Marko exemple. Dans ces quatre exemples, on ne compile pas, le reste de produire la même liste du même type.

List<Class<? extends Reference>> list = new ArrayList<>();
list.add(SoftReference.class);
list.add(WeakReference.class);
list.add(PhantomReference.class);

List<Class<? extends Reference>> list = new ArrayList<>(
     Arrays.asList(SoftReference.class));
list.add(WeakReference.class);
list.add(PhantomReference.class);

List<Class<? extends Reference>> list = new ArrayList<>(
     Arrays.asList(SoftReference.class, WeakReference.class));
list.add(PhantomReference.class);

List<Class<? extends Reference>> list = new ArrayList<>(
     Arrays.asList(SoftReference.class, WeakReference.class, PhantomReference.class));

14voto

Ted Hopp Points 122617

Problème intéressant. Je pense que ce qui se passe est la suivante. Lorsque vous avez deux éléments comme ceux que vous montrez, le type de retour de asList est le type le plus spécifique de tous les arguments, qui dans votre premier exemple est List<Reference> . Ceci est compatible avec les affectations List<? extends Reference> . Lorsque vous avez un seul argument, le type de retour est le type spécifique de l'argument, qui n'est pas compatible avec les affectations, car les génériques ne sont pas covariants.

9voto

irreputable Points 25577

Envisager

    // ok
    List<Object> list3 = Arrays.asList(new Object(), new String());
    // fail
    List<Object> list4 = Arrays.asList(new String());

Le 2ème exemple tente d'attribuer une List<String> d'un List<Object>, qui échoue.

Le 2ème exemple, pourrait travailler, si javac regarde le contexte environnant, prend en compte le type de cible, et d'en déduire qu' T=Object serait de travailler ici. Java 8 sera probablement le faire (je ne suis pas sûr)

Dans une seule situation, javac (java 5) utilisation contextuelle info pour l'inférence de type, voir http://docs.oracle.com/javase/specs/jls/se7/html/jls-15.html#jls-15.12.2.8

Nous pouvons en profiter pour faire une solution de contournement

public static <R, T extends R> List<R> toList(T... elements)
{
    return Arrays.asList((R[])elements);
}

Maintenant, ils peuvent être compilés:

    List<Object> list4 = toList(new String());

    List<Class<? extends Reference>> list = toList(SoftReference.class, WeakReference.class);

    List<Class<? extends Reference>> list2 = toList(WeakReference.class);

C'est parce qu' R ne peut pas être déduit de l'argument, les types et la méthode, le résultat est dans une affectation, afin de javac essaie d'en déduire R par le type de cible.

Cela fonctionne de la cession, ou dans une instruction de retour

List<Class<? extends Reference>> foo()
{
    return toList(WeakReference.class);  // "subject to assignment conversion"
}

Il ne fonctionnera pas sinon

void bar(List<Class<? extends Reference>> list){...}

bar( toList(WeakReference.class) ); // fail; R not inferred

4voto

Marko Topolnik Points 77257

Il y a deux parties à l'explication de ce comportement:

  1. Comment le type du côté droit de changer avec l'évolution des arguments?
  2. Pourquoi sont quelques-uns des RHS types incompatibles avec la LHS type?

1. Le Côté Droit

La signature de l' asList est

<T> List<T> asList(T... a)

Cela signifie que tous les arguments doivent être regroupés dans un seul type T, ce qui est le plus spécifique de type pour les types de tous les arguments. Dans ce cas particulier, nous avons

asList(WeakReference.class) -> List<Class<WeakReference>>

et

asList(WeakReference.class, SoftReference.class) 
   -> List<Class<? extends Reference>>

Ces deux sont assez évident.

2. La Gauche

Maintenant, pourquoi ne peut-on pas attribuer la première expression, de type List<Class<WeakReference>>, pour une variable de type List<Class<? extends Reference>>? La meilleure façon de comprendre pourquoi les règles doivent être est la preuve par contradiction. Considérez les points suivants:

  • List<Class<? extends Reference>> a add(Class<? extends Reference>)
  • List<Class<WeakReference>> a add(Class<WeakReference>).

Maintenant, si Java vous a permis de vous en attribuer un à l'autre:

List<Class<WeakReference>> lw = new ArrayList<>();
List<Class<? extends Reference>> lq = lw;
lq.add(PhantomReference.class);

il en résulterait une violation claire de sécurité de type.

2voto

Hanno Binder Points 3880

Ce qui est intéressant:

where INT#1 is an intersection type:
INT#1 extends AbstractList,Cloneable,Serializable

Peut-être que c'est la cause pour (certains) des questions?

L' intersection type des éléments peut ne pas être déterminée de manière unique. Lorsque vous déclarez votre propre liste MyList<T> implements List<T>, l'intersection type du tableau est déterminé comme List<T>.

Lors de l'utilisation d' Arrays.<Class<? extends List>>asList(ArrayList.class, LinkedList.class); le 'carrefour' type est indiqué explicitement (comme List) et n'a pas besoin d'être inférée par le compilateur.

D'ailleurs, je crois que ce que Ted Hopp a dit est correct pour les autres cas.

EDIT:

La différence entre

List<Class<? extends Reference>> list2 = Arrays.asList(WeakReference.class);

et

List<Class<? extends Reference>> list3 = Arrays.<Class<? extends Reference>>asList(WeakReference.class);

peut être le point dans le temps lorsque le compilateur détermine la nouvelle liste du type: je me dis qu'il doit déterminer le type générique de la liste avant compte tenu de la cession. Pour cela, il prend l'information qu'il a à déduire le type de la nouvelle liste sans égard à la mission. Cela peut entraîner deux types de liste créé par les deux instructions ci-dessus, résultant dans le comportement observé.

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