Problème de conflit entre une méthode générique et le type générique d'une classe

Le
Laurent Courtin
Bonjour,

J'ai un problème de compilation, tout ce passe comme si il y avait un
conflit entre une méthode générique (type T) et le type générique
d'une classe (type E) quand il n'est pas définit. (Voir l'exemple)

Quand je n'indique pas le type E (ce que je veux pouvoir faire la
plupart du temps) la détection du type de retour de la méthode
remoteCall ne se compile plus.

Avez vous une idée du pourquoi ?
Avez vous une autre manière de l'écrire ?

Bien sur je pourrai extraire les méthodes utilisant le type E dans ne
classe étendant AbstractRemote, puis étendre cette dernière classe
lorsque j'ai besoin de préciser le type E.

J'ai joint un exemple montrant le problème.

Bien cordialement,

Laurent



<source>
import java.awt.Color;

public class Test {

public interface Action {
String maMethode1();
Color maMethode2();
}

public class AbstractRemote<E> {
protected <T> T remoteCall (Object pars) {
// .
return null;
}

protected E uneMethode() {
//
return null;
}
}

public class MyRemoteA extends AbstractRemote<Object> implements
Action {
public String maMethode1() {
return remoteCall();
}

public Color maMethode2() {
return remoteCall();
}
}

public class MyRemoteB extends AbstractRemote implements Action {
public String maMethode1() {
return remoteCall();
// Ne se compile pas :
// incompatible types
// found : java.lang.Object
// required: java.lang.String
}

public Color maMethode2() {
return remoteCall();
// Ne se compile pas ici non plus
}
}
}
</source>
Vidéos High-Tech et Jeu Vidéo
Téléchargements
Vos réponses
Gagnez chaque mois un abonnement Premium avec GNT : Inscrivez-vous !
Trier par : date / pertinence
TestMan
Le #229165
Bonjour,


<source>
import java.awt.Color;

public class Test {

public interface Action {
String maMethode1();
Color maMethode2();
}

public class AbstractRemote<E> {
protected <T> T remoteCall (Object... pars) {
// ....
return null;
}

protected E uneMethode() {
// ...
return null;
}
}

public class MyRemoteA extends AbstractRemote<Object> implements
Action {
public String maMethode1() {
return remoteCall();
}

public Color maMethode2() {
return remoteCall();
}
}

public class MyRemoteB extends AbstractRemote implements Action {
public String maMethode1() {
return remoteCall();
// Ne se compile pas :
// incompatible types
// found : java.lang.Object
// required: java.lang.String
}

public Color maMethode2() {
return remoteCall();
// Ne se compile pas ici non plus
}
}
}
</source>



Bonjour,

Mettez simplement T en paramètre de la classe et supprimez le "<T>" au
niveau de la méthode. Restera à transtyper "à l'ancienne" pour les cas
ou vous ne voulez pas de paramètres sur votre classe.

En clair, un code du style :


class Test {

public interface Action {
String maMethode1();
Color maMethode2();
}

public class AbstractRemote<E,T> {
protected T remoteCall (Object... pars) {
// ....
return null;
}

protected E uneMethode() {
// ...
return null;
}
}

class MyRemoteA extends AbstractRemote implements
Action {
public String maMethode1() {
return (String)remoteCall();
}

public Color maMethode2() {
return (Color)uneMethode();
}
}

class MyRemoteB extends AbstractRemote<Color,String> implements Action {
public String maMethode1() {
return remoteCall();
}

public Color maMethode2() {
return uneMethode();
}
}
}

A+
TM

Laurent Courtin
Le #229163
Merci,

Le transtypage (cast) est justement ce que je voulais éviter.
De plus dans le solution que vous proposez ça ne sert plus à rien
d'utiliser le type générique T.

La construction de la méthode générique remoteCall est pratique car
elle "découvre" automatiquement le type de retour : String pour
maMethode1 et Color pour maMethode2.

J'aimerai comprendre ce qui ce passe quand on laisse le compilateur
utiliser la valeur par défaut du type générique E.

Cordialement,

--
Laurent
Maxime Daniel
Le #229129
On 20 juin, 14:47, Laurent Courtin
public class MyRemoteB extends AbstractRemote implements Action {
public String maMethode1() {
return remoteCall();
// Ne se compile pas :
// incompatible types
// found : java.lang.Object
// required: java.lang.String
}
Si vous êtes intéressé par la raison derrière le message d'erreur,

vous pouvez vous reporter à la spécification. Le compilateur doit
inférer le type T d'après les types des paramètres de la méthode. Il
n'y en a pas. Donc il déduit la contrainte minimale sur T, soit
Object. Ce type n'est pas un type de retour valide pour maMethode1,
d'où l'erreur. Il est troublant que le type attendu pour l'expression
qui inclut l'appel de remoteCall ne compte pas pour la détermination
de T, mais aussi peu intuitif que cela puisse paraître, c'est la spec.

TestMan
Le #229093
Merci,

Le transtypage (cast) est justement ce que je voulais éviter.
De plus dans le solution que vous proposez ça ne sert plus à rien
d'utiliser le type générique T.

La construction de la méthode générique remoteCall est pratique car
elle "découvre" automatiquement le type de retour : String pour
maMethode1 et Color pour maMethode2.

J'aimerai comprendre ce qui ce passe quand on laisse le compilateur
utiliser la valeur par défaut du type générique E.

Cordialement,

--
Laurent



Bonjour,

Pour compléter l'explication de Maxime, en qui concerne :

public class AbstractRemote<E> {
protected <T> T remoteCall (Object... pars) {
// ....
return null;
}

protected E uneMethode() {
// ...
return null;
}
}

Vu que le compilo n'analyse pas le Type de T (pas en parametre ni au
niteau de la classe ni au niveau de la méthode), il en resulte que T est
donc un Object. Au final lorsque vous testez :

public class MyRemoteB extends AbstractRemote implements Action {
public String maMethode1() {
return remoteCall();
}
}

Vous vous retrouvez avec un erreur qui vous indique que vous tentez
d'assigner un Object (le retour du remoteCall) dans un chaine...

Il faut donc, soit préciser le type avec un transtypage (méthode
traditionelle), soit identifier qu'au final cet élement est un parametre
de la classe AbstractRemote et le mettre en tant que tel (voir mon
précedent exemple).

A+
TM

Laurent Courtin
Le #229088
On 22 juin, 11:45, Maxime Daniel
On 20 juin, 14:47, Laurent Courtin
public String maMethode1() {
return remoteCall();
// Ne se compile pas :
// incompatible types
// found : java.lang.Object
// required: java.lang.String
}


Si vous êtes intéressé par la raison derrière le message d'erreur,
vous pouvez vous reporter à la spécification. Le compilateur doit
inférer le type T d'après les types des paramètres de la méthode. Il
n'y en a pas. Donc il déduit la contrainte minimale sur T, soit
Object. Ce type n'est pas un type de retour valide pour maMethode1,
d'où l'erreur. Il est troublant que le type attendu pour l'expression
qui inclut l'appel de remoteCall ne compte pas pour la détermination
de T, mais aussi peu intuitif que cela puisse paraître, c'est la spec.


Merci de vos réponses.

Ce que je ne comprends pas c'est que ceci fonctionne (quand on donne
explicitement une valeur au type E):
Dans ce cas le type T est inféré à partir de la valeur de retour de la
méthode.
public class MyRemoteA extends AbstractRemote<UnTypeQuelquonque>
implements
Action {
public String maMethode1() {
return remoteCall();
}

public Color maMethode2() {
return remoteCall();
}
}

Alors que si j'omets la valeur de E, la compilation provoque une
erreur (ce qui est la cas dans MyRemoteB).

Cordialement,


Maxime Daniel
Le #228998
On 25 juin, 09:15, Laurent Courtin
On 22 juin, 11:45, Maxime Daniel


On 20 juin, 14:47, Laurent Courtin
public String maMethode1() {
return remoteCall();
// Ne se compile pas :
// incompatible types
// found : java.lang.Object
// required: java.lang.String
}


Si vous êtes intéressé par la raison derrière le message d'erre ur,
vous pouvez vous reporter à la spécification. Le compilateur doit
inférer le type T d'après les types des paramètres de la méthod e. Il
n'y en a pas. Donc il déduit la contrainte minimale sur T, soit
Object. Ce type n'est pas un type de retour valide pour maMethode1,
d'où l'erreur. Il est troublant que le type attendu pour l'expression
qui inclut l'appel de remoteCall ne compte pas pour la détermination
de T, mais aussi peu intuitif que cela puisse paraître, c'est la spec.


Merci de vos réponses.

Ce que je ne comprends pas c'est que ceci fonctionne (quand on donne
explicitement une valeur au type E):
Dans ce cas le type T est inféré à partir de la valeur de retour de la
méthode.
public class MyRemoteA extends AbstractRemote<UnTypeQuelquonque>
implements
Action {
public String maMethode1() {
return remoteCall();
}

public Color maMethode2() {
return remoteCall();
}
}

Alors que si j'omets la valeur de E, la compilation provoque une
erreur (ce qui est la cas dans MyRemoteB).

Cordialement,


Vous avez raison, j'ai dit une bêtise plus haut. JLS 15.12.2.8
explique bel et bien que, si il reste des types paramètres pour une
méthode qui ne sont pas inférés après considération de tous les
paramètres de la méthode, et que le résultat de la méthode est util isé
dans un contexte d'affectation, alors le type attendu pour
l'expression est réintroduit comme contrainte pour l'inférence des
types paramètres non inférés. En simple, le type de retour peut, en
dernier recours, être considéré dans l'inférence, et c'est ce qui
arrive pour MyRemoteA.

Pour MyRemoteB, l'explication provient du fait que dans MyRemoteB
extends AbstractRemote, AbstractRemote est un raw type (un type
générique pour les types formels duquel on n'a pas donné de types
effectifs), et que comme le type de remoteCall pour ce raw type est
l'erasure du remoteCall déclaré par AbstractRemote (JLS 3 p. 59), soit
Object remoteCall(Object... args), les appels de remoteCall dans
MyRemoteB sont de type Object, sans qu'aucune inférence n'ait lieu.

Encore désolé d'avoir avancé une explication saugrenue.



Publicité
Poster une réponse
Anonyme