IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Langage Java Discussion :

Généricité et héritage


Sujet :

Langage Java

  1. #1
    Membre régulier
    Inscrit en
    Novembre 2003
    Messages
    245
    Détails du profil
    Informations forums :
    Inscription : Novembre 2003
    Messages : 245
    Points : 106
    Points
    106
    Par défaut Généricité et héritage
    Bonjour,

    J'ai des avertissements Java sur une fonction statique un peu complexe niveau généricité.

    La classe sert à valider des "entités" Hibernate grâce à la classe ClassValidator<Entity>(Entity.class) (qui doit être instanciée pour chaque entité). Or, pour ne pas avoir à créer le ClassValidator à chaque fois, je gère un cache.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    public final class DomainValidator {
     
    	/** Validators cache (for each Entity class name) */
    	private static final Map<String, ClassValidator<? extends AbstractEntity>> VALIDATORS = new TreeMap<String, ClassValidator<? extends AbstractEntity>>();
     
    	/** Get the Entity validator in the cache, or create it if it does not already exist */
    	private static <Entity extends AbstractEntity> ClassValidator<Entity> getValidator(Entity entity) {
    		Class<Entity> clazz = (Class<Entity>) entity.getClass(); // Avertissement 1
    		String className = clazz.getName();
    		ClassValidator<Entity> validator = null;
    		synchronized (VALIDATORS) {
    			validator = (ClassValidator<Entity>) VALIDATORS.get(className); // Avertissement 2
    			if (validator == null) {
    				validator = new ClassValidator<Entity>(clazz);
    				VALIDATORS.put(className, validator);
    			}
    		}
    		return validator;
    	}
     
    	/** Validate an Entity */
    	public static <Entity extends AbstractEntity> Collection<InvalidValue> validate(Entity entity, String... properties) {
    		// ...
    	}
     
    	private DomainValidator() {
    		//
    	}
    }
    • Avertissement 1 : Type safety: Unchecked cast from Class<capture#1-of ? extends AbstractEntity> to Class<Entity>
    • Avertissement 2 : Type safety: Unchecked cast from ClassValidator<capture#2-of ? extends AbstractEntity> to ClassValidator<Entity>

    Comment ce code peut-il être amélioré ?

    Merci.

  2. #2
    Membre émérite
    Avatar de gifffftane
    Profil pro
    Inscrit en
    Février 2007
    Messages
    2 354
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire (Rhône Alpes)

    Informations forums :
    Inscription : Février 2007
    Messages : 2 354
    Points : 2 582
    Points
    2 582
    Par défaut
    Trop de généricité tue la généricité, il faut le dire et le répéter.

    Et trop de répétition tue la répétition, je l'affime aussi hautement.

    Bon, pour l'avertissement 1, sous réserve de la déclaration du constructeur deClassValidator il est possible d'écrire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    Class<?> clazz = entity.getClass(); // Avertissement 1 REGLE
    Pour le 2, il me faudrait la déclaration de VALIDATORS.get, mais j'ai peur que tu demandes l'impossible.

    Peut être cela fonctionne avec une signature type

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    <T extends AbstractEntity> ClassValidator<T> get(String className);
    ... mais je crains que ce ne soit pas ce que tu cherches.

    Dans l'idéal, pourrais-tu simplifier ton code avant de nous le soumettre ? (un truc sous la forme d'un fichier qui compile ? )

    Attention que jongler avec les classes en contexte générique java c'est courir au devant des ennuis. En langage intellectuel c'est contre la philosophie, en langage pratique c'est contre l'implémentation.

    Comment ce code peut-il être amélioré ? Trop de généricité... En ce genre de cas je me crée une classe non générique qui hérite de parent générique, cela me simplifie au moins l'écriture.

  3. #3
    Membre régulier
    Inscrit en
    Novembre 2003
    Messages
    245
    Détails du profil
    Informations forums :
    Inscription : Novembre 2003
    Messages : 245
    Points : 106
    Points
    106
    Par défaut
    Je serais tenté de dire que le problème à la source vient du ClassValidator d'Hibernate, qui prend à la fois un type générique, et son object Class. Ça a tendance à faire double emploi, et m'oblige à cette gymnastique, qui fonctionne sans être totalement satisfaisante.

  4. #4
    Membre régulier
    Inscrit en
    Novembre 2003
    Messages
    245
    Détails du profil
    Informations forums :
    Inscription : Novembre 2003
    Messages : 245
    Points : 106
    Points
    106
    Par défaut
    Voici des classes simplifiées, comme suggéré :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    /** Class validator */
    public final class ClassValidator<T> {
     
    	public ClassValidator(Class<T> beanClass) {
    		//
    	}
     
    	public String[] validate(T bean) {
    		return new String[] {};
    	}
    }
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    import java.util.Map;
    import java.util.TreeMap;
     
    public final class MyValidator {
     
    	abstract class AbstractEntity {
    		// Entities will inherit this class
    	}
     
    	/** Validators cache (for each Entity class name) */
    	private static final Map<String, ClassValidator<? extends AbstractEntity>> VALIDATORS = new TreeMap<String, ClassValidator<? extends AbstractEntity>>();
     
    	/** Get the Entity validator in the cache, or create it if it does not already exist */
    	private static <Entity extends AbstractEntity> ClassValidator<Entity> getValidator(Entity entity) {
    		Class<Entity> clazz = (Class<Entity>) entity.getClass();
    		String className = clazz.getName();
    		ClassValidator<Entity> validator = (ClassValidator<Entity>) VALIDATORS.get(className);
    		if (validator == null) {
    			validator = new ClassValidator<Entity>(clazz);
    			VALIDATORS.put(className, validator);
    		}
    		return validator;
    	}
     
    	/** Validate an Entity */
    	public static <Entity extends AbstractEntity> String[] validate(Entity entity) {
    		return getValidator(entity).validate(entity);
    	}
    }
    Mes contraintes sont :
    • ClassValidator ne m'appartient pas
    • mes entités héritent d'AbstractEntity
    • je souhaiterais pouvoir les valider sans avoir à créer explicitement de ClassValidator

    Merci de votre aide.

  5. #5
    Expert éminent sénior
    Avatar de adiGuba
    Homme Profil pro
    Développeur Java/Web
    Inscrit en
    Avril 2002
    Messages
    13 938
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Développeur Java/Web
    Secteur : Transports

    Informations forums :
    Inscription : Avril 2002
    Messages : 13 938
    Points : 23 190
    Points
    23 190
    Billets dans le blog
    1
    Par défaut
    Salut,


    L'objectif des Generics est de fournir un code typesafe, et en toute logique cela s'adapte mal à la reflection...

    Ce type de warning prévient que le compilateur ne peut pas garantir la logique de l'ensemble, et il signale donc un problème potentiel.



    Pour la première ligne, le problème vient du fait que le type réel de ton paramètre 'entity' peut être différent du type Generics déclaré.
    Par exemple si tu as deux classes comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    class EntityA extends AbstractEntity { }
    class EntityB extends EntityA { }
    Tu pourrais très bien faire ceci sans avoir d'erreur de compilation :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    	EntityA a = new EntityB();
    	ClassValidator<EntityA> validator = DomainValidator.getValidator(a);
    Comme 'a' a pour type reel EntityB, tu obtiens un ClassValidator<EntityB> déclaré en ClassValidator<EntityA>... ce qui risque de provoquer un beau casse-tête



    Pour mieux te renseigner il faudrait avoir plus d'info sur la classe ClassValidator, en particulier sur son type paramétré T, car dans le code que tu donnes il n'est jamais utilisé

    a++

  6. #6
    Membre émérite
    Avatar de gifffftane
    Profil pro
    Inscrit en
    Février 2007
    Messages
    2 354
    Détails du profil
    Informations personnelles :
    Localisation : France, Loire (Rhône Alpes)

    Informations forums :
    Inscription : Février 2007
    Messages : 2 354
    Points : 2 582
    Points
    2 582
    Par défaut
    Excuse je ne suis pas un familier d'Hibernate.

    La proposition suivante n'est sans doute pas satisfaisante, mais j'aimerais savoir ce qui te gênerait en la suivant ; elle consiste à se passer de tous les ? (sauf 1) :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
      static Map<String, ClassValidator<AbstractEntity>> VALIDATORS;
     
    	private static ClassValidator<AbstractEntity> getValidator(
          AbstractEntity entity) 
      {
    		Class<?> clazz = entity.getClass();
    		String className = clazz.getName();
    		ClassValidator<AbstractEntity> validator = VALIDATORS.get(className);
    		if (validator == null) 
        {
    			validator = new ClassValidator<AbstractEntity>(clazz);
    			VALIDATORS.put(className, validator);
    		}
    		return validator;
    	}
    ... donc dans le code d'appel de getValidator, tu n'as plus de distinguo sur le AbstractEntity, puisque c'est tous des AbstractEntity. Cela est-il génant, souhaites-tu désigner la classe exacte ?

  7. #7
    Membre régulier
    Inscrit en
    Novembre 2003
    Messages
    245
    Détails du profil
    Informations forums :
    Inscription : Novembre 2003
    Messages : 245
    Points : 106
    Points
    106
    Par défaut
    Citation Envoyé par gifffftane Voir le message
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
     
    Class<?> clazz = entity.getClass();
    validator = new ClassValidator<AbstractEntity>(clazz);
    Ceci ne fonctionne pas car "?" et AbstractEntity sont incompatibles.

    J'ai fini par arriver à une solution acceptable.

    1. Je fournis désormais l'objet Class avec le bean, car la fonction getClass renvoie toujours un objet de type Class<? extends Bean>.
    2. Je stocke les ClassValidator<...> dans la Map en temps que ClassValidator (avec SuppressWarnings). Une méthode de cast générique (avec SuppressWarnings) me permet ensuite de convertir l'objet sans avertissement.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    @SuppressWarnings("unchecked")
    private static final Map<String, ClassValidator> VALIDATORS = new TreeMap<String, ClassValidator>();
     
    private static <Entity extends AbstractEntity> ClassValidator<Entity> getValidator(Class<Entity> entityClass) {
    	String className = entityClass.getName();
    	ClassValidator<Entity> validator = GenericsTools.cast(VALIDATORS.get(className));
    	if (validator == null) {
    		validator = new ClassValidator<Entity>(entityClass);
    		VALIDATORS.put(className, validator);
    	}
    	return validator;
    }
     
    /** Validate an Entity */
    public static <Entity extends AbstractEntity> String[] validate(Class<Entity> entityClass, Entity entity) {
    	ArgumentTools.checkClass(entity, entityClass);
    	return getValidator(entityClass).validate(entity);
    }

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. HashMap, généricité, wildcard, héritage, extension
    Par kenji_getpowered dans le forum Collection et Stream
    Réponses: 2
    Dernier message: 01/04/2010, 17h40
  2. Héritage, Généricité, Cast?!
    Par aroussi_sanaa dans le forum Général Java
    Réponses: 1
    Dernier message: 04/05/2009, 11h10
  3. Généricité, héritage et Reflexion/introspection
    Par K-Kaï dans le forum Langage
    Réponses: 3
    Dernier message: 26/02/2009, 14h27
  4. Question de généricité et d'héritage
    Par fatypunk dans le forum Langage
    Réponses: 7
    Dernier message: 22/06/2008, 13h12
  5. Généricité et héritage
    Par TiteMarie dans le forum Langage
    Réponses: 3
    Dernier message: 27/05/2008, 12h17

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo