Aller au contenu

Types génériques en Java.


Sharpshooter

Messages recommandés

Bonjour,

J'ai une question à propos des types génériques en Java (des fois que Gfx trainerait encore ici :D).

J'ai une classe qui commence comme ceci :

public class Personne

dans laquelle il y a un objet comme ceci :

P profession;

Le problème est que je ne peux pas accéder aux méthodes de cet objet comme ceci par exemple :

String nom = profession.getNom();

Le compilateur me dit qu'il ne trouve pas la méthode getNom() dans java.lang.Object ce qui me parait normal. Mais pourquoi mon objet profession a-t-il perdu le type P pour redevenir de type Object ? Je précise qu'à l'initialisation P est bien de type Medecin, Enseignant, ...

Est-ce une limitation de la généricité dans Java ou suis-je passé à côté de quelque chose ?

Lien à poster
  • Réponses 50
  • Créé
  • Dernière réponse

Meilleurs contributeurs dans ce sujet

Meilleurs contributeurs dans ce sujet

Les generics de Java ne sont pas les templates du C++ :-D Java utilise la type erasure : au runtime les generics sont traités comme des Object. Pour résoudre ton problème voici ce qu'il faut faire :

public class Personne

Maintenant le compilateur sait que P sera forcément du type Profession (dans lequel tu ne manqueras pas de déclarer la méthode getNom()).

Java utilise cette approche pour éviter le latent typing qui conduit à bien des problèmes.

Plus d'infos ici : http://www.progx.org/index.php?section=replies&newsid=212

Lien à poster

Hum... Ca marche mais j'utilise la méthode getNom() de la classe Profession, or je voudrais pouvoir utiliser celle de la classe P.

Si j'ai bien compris ce que tu dis ce n'est pas possible ?

En fait je voudrais pouvoir créer une personne qui soit médecin ou enseignant ou autre. La classe personne sera la plus utilisée je ne veux donc pas créer une classe médecin qui hérite de personne mais plutôt une sorte de classe personne qui hérite de médecin ou d'instituteur selon le cas.

Je ne sais pas si je suis très clair. Faudrait que je dorme un peu :-D

Lien à poster

L'approche est bizarre je pense. Un médecin ou un instituteur sont des genres de personnes, personne devrait donc être la super classe. A mon avis si tu résonnes différemment tu vas arriver à quelque chose de tordu. Ou alors je n'ai pas compris ton problème.

Après tu peux peut être résoudre ça avec des interfaces médecin, instituteur, et ta personne implemente l'une ou l'autre...

Sinon Gfx nous sortira bien une notion de derrière les fagots :-D

@+

Lien à poster

Oui je sais, je prends un peu le problème à l'envers mais en fait la classe Personne sera utilisée sans cesse et contient beaucoup de données alors que la classe Medecin ne rajoute qu'un ou deux trucs.

Je vois plus la classe Médecin comme une caractéristique de la personne, pas comme un objet à part entière à utiliser. C'est pour ça que les types génériques m'allaient bien. :-D

Tant pis je vais faire autrement.

Lien à poster

Bah si tu fais

public class Personne {

Profession p;

}

où Profession est une interface

public interface Profession {

public String getNom();

}

et où Medecin, Enseignant implémentent Profession, ça ne répond pas au problème ?

Alors après tu avais peut être une vision particulière d'utilisation par les generics, mais j'avoue ne pas les avoir trop utilisé pour bien en parler.

@+

Lien à poster

Je suis pas trop au jus à propos des patterns (faudrait mais bon).

La généricité c'est depuis java 1.5 avec plein d'autres trucs hyper intéressants comme les enumérations qui évitent les constantes à tours de bras, les transtypages automatiques pour certains types (Int en int, ...), des méthodes avec un nombre d'arguments variables et la possibilité d'importer les membres statiques d'un package.

Bah je voulais utiliser les types générique parce que ça me semblait sympa mais tant pis je vais faire de profession une variable quelconque (de type Profession) et j'utiliserai un enum et un switch / case. J'aime pas trop mais bon.

Lien à poster

Sharpshooter : Il n'y a pas de problème avec ma solution. Si tu surcharges getNom() dans les classes qui héritent de Profession, ce sont ces méthodes qui seront appelée, pas celle de Profession. Révises ta POO :-D Java n'est pas comme le C++ avec les méthodes virtuelles et autres. Exemple :

class A {
 public String getNom() { return "A"; }
}

class B extends A {
 public String getNom() { return "B"; }
}

A objet = new B();
objet.getNom(); // Renvoie "B", pas "A"

Lien à poster

Pfff ! Étant donné que le nom de la profession ne change pas je l'avais mis en "static final"... et du coup son accesseur aussi ! Quand je disais qu'il fallait que je dorme.

Bien évidemment j'avais regardé sur Progx avant de poster ici mais je n'avais pas trouvé l'article sur les classes génériques ...

... Merci à tous pour tout. Je vais adopter la solution de Gfx et aller me coucher :-D

Lien à poster

En tout cas ça marche maintenant (avec la solution de Gfx). :-D

La généricité est vraiment très pratique.

Du coup j'ai une autre question : est-il possible d'effectuer une initialisation de la forme P profession = new P(). ? Bien entendu tel quel j'ai essayé et ça ne marche pas.

Java est décidément un langage très élégant.

Lien à poster

Pourquoi tu veux faire ça? J'ai l'impression que tu veux absolument utiliser des generics alors que tu n'en as pas forcément besoin.

Sinon oui Java c'est élégant comme langage, ça fait 15 jours que je me bats avec du C# et j'ai du mal avec les 462 paramètres qu'il faut passer pour instancier le moindre truc.

Lien à poster

Sharpshooter, thev> Pour faire votre truc voici la marche à suivre :

public class Conteneur {
 public T createInstance(Class klass) {
   try {
     return klass.newInstance();
   } catch (InstantiationException ie) {
     return null;
   } catch (IllegalAccessException iae) {
     return null;
   }
 }
}

C'est juste un exemple pour montrer qu'on doit avoir un Class pour faire une instance de T.

Sharp, tu peux jeter un oeil au code source de ma lib Fuse (http://fuse.dev.java.net) qui est générifiée à mort pour transformer du texte en object Java en fonction de types découverts par introspection.

Lien à poster

Comment obtient-on un Class à partir de T (cette question peut paraître bizarre car des choses m'échappent) ?

Faudra que je regarde les generics de manière plus approfondie car je vois que je n'en connais rien (hormis la simple utilisation à travers les Collection et Cie.)

@+

Lien à poster

Heu... il y a un truc qui m'échappe. Ce bout de code :

public class Conteneur {
 public T createInstance(Class klass) {
   try {
     return klass.newInstance();
   } catch (InstantiationException ie) {
     return null;
   } catch (IllegalAccessException iae) {
     return null;
   }
 }
}

Permet de créer une instance de type T quelque soit le T que l'on passe en paramètre... mais du coup il faut passer T en paramètre de la classe Conteneur. non ?

Lien à poster

Oui : Machin m = new Conteneur().createNewInstance(Machin.class);

Cela dit on peut aussi faire ceci :

public class Conteneur {
 public static  T createInstance(Class klass) throws Exception {
   return klass.newInstance();
 }

 public static void main(String... args) throws Exception {
   System.out.println(Conteneur.createInstance(javax.swing.JButton.class));
 }
}

C'est juste très con cette façon-là puisqu'en appelan createInstance() on connaît forcément le type, donc autant faire un Class<?>.newInstance() nous-mêmes. Là où ça devient intéressant, c'est avec les jokers :

public class Conteneur {
 public static  T createInstance(Class klass) throws Exception {
   return klass.newInstance();
 }

 public static void main(String... args) throws Exception {
   // pass
   System.out.println(Conteneur.createInstance(javax.swing.JButton.class));
   // fail
   System.out.println(Conteneur.createInstance(java.io.File.class));
 }
}

Dans ce cas, la méthode createInstance() ne peut-être appelée que lorsque le type T passé en paramètre hérite de JComponent, ce qui n'est bien sûr pas le cas de la classe File.

Dans tous les cas, ces exemples montrent deux choses :

- On peut créer une instance à partir d'un type T à condition d'avoir également le Class correspondant ;

- Cela ne sert pour ainsi dire à rien d'avoir une telle fonctionnalité dans une API publique.

J'ai écrit du code qui utilise cette technique mais ce n'est qu'une partie d'un process plus important et ce n'est pas une API publique.

Lien à poster

Je vais sûrement dire une bétise mais est-ce qu'on ne pourrait pas faire un truc du genre :

class Personne

{
    public static Personne creer(Medecin m)
    {
          return new Personne();
    }

   public static Personne creer(Enseignant e)
   {
         return new Personne();
   }

    ...
}

Une sorte de constructeur bis qui permettrait de faire appel à : creer(profession) et la personne serait créée quel uqe soit le type de profession.

Lien à poster

×
×
  • Créer...