Twitter iPhone pliant OnePlus 11 PS5 Disney+ Orange Livebox Windows 11

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

6 réponses
Avatar
Laurent Courtin
Bonjour,

J'ai un probl=E8me de compilation, tout ce passe comme si il y avait un
conflit entre une m=E9thode g=E9n=E9rique (type T) et le type g=E9n=E9rique
d'une classe (type E) quand il n'est pas d=E9finit. (Voir l'exemple)

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

Avez vous une id=E9e du pourquoi ?
Avez vous une autre mani=E8re de l'=E9crire ?

Bien sur je pourrai extraire les m=E9thodes utilisant le type E dans ne
classe =E9tendant AbstractRemote, puis =E9tendre cette derni=E8re classe
lorsque j'ai besoin de pr=E9ciser le type E.

J'ai joint un exemple montrant le probl=E8me.

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>

6 réponses

Avatar
TestMan
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

Avatar
Laurent Courtin
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
Avatar
Maxime Daniel
On 20 juin, 14:47, Laurent Courtin wrote:
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.

Avatar
TestMan
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

Avatar
Laurent Courtin
On 22 juin, 11:45, Maxime Daniel wrote:
On 20 juin, 14:47, Laurent Courtin wrote:> pub lic 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.


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,


Avatar
Maxime Daniel
On 25 juin, 09:15, Laurent Courtin wrote:
On 22 juin, 11:45, Maxime Daniel wrote:



On 20 juin, 14:47, Laurent Courtin wrote:> p ublic 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'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.