Récupérer dynamiquement les classes des types génériques

KX Messages postés 16761 Date d'inscription   Statut Modérateur Dernière intervention   -  
KX Messages postés 16761 Date d'inscription   Statut Modérateur Dernière intervention   -
Bonjour,

Je voudrais encapsuler dans un objet Converter<X,Y> soit une méthode static qui prends un paramètre X et renvoie un objet Y, soit un constructeur de la classe Y qui prend en paramètre la classe X, afin ensuite de pouvoir convertir un objet X en Y, en s'assurant de la cohérence des types.

Actuellement mon code fonctionne, voici sa structure :

private static class Converter<X,Y>
{
    private final Method method;
    private final Constructor<Y> constructor;
    
    /** @param method : public static Y method(X) */
    private Converter(Class<Y> y, Method method, Class<X> x);
    
    /** @param constructor : public Y(X) */
    private Converter(Constructor<Y> constructor, Class<X> x);
    
    private Y parse(X x);
}

Le problème c'est que pour l'instant je suis obligé de passer en paramètres des objets de types Class<X> et Class<Y> pour faire mes tests, j'aimerais pouvoir les obtenir directement à partir de mes types génériques <X,Y> afin de simplifier ma structure et avoir seulement ceci :

private static class Converter<X,Y>
{
    private final Method method;
    private final Constructor<Y> constructor;
    
    /** @param method : public static Y method(X) */
    private Converter(Method method);
    
    /** @param constructor : public Y(X) */
    private Converter(Constructor<Y> constructor);
    
    private Y parse(X x);
}

Je ne sais pas si c'est possible, en tout cas je n'ai pas trouvé comment contourner le problème.
Je prends donc volontiers toute idée sur la question. Merci d'avance.

Voici le code complet pour voir pourquoi j'ai actuellement besoin de ces objets :

private static class Converter<X,Y>
{
    private final Method method;
    private final Constructor<Y> constructor;
    
    /** @param method : public static Y method(X) */
    private Converter(Class<Y> y, Method method, Class<X> x) throws IllegalArgumentException
    {
        if (method==null)
            throw new IllegalArgumentException("method can't be null");

        int modifiers = method.getModifiers();

        if (!Modifier.isPublic(modifiers))
            throw new IllegalArgumentException("method must be public: "+method.getName());

        if (!Modifier.isStatic(modifiers))
            throw new IllegalArgumentException("method must be static: "+method.getName());

        if (method.getParameterTypes().length!=1)
            throw new IllegalArgumentException("method must take one and only one argument: "+method.getName());

        if (!method.getParameterTypes()[0].isAssignableFrom(x))
            throw new IllegalArgumentException("method must have a compatible argument: "+x+", "+method.getName());

        if (!y.isAssignableFrom(method.getReturnType()))
            throw new IllegalArgumentException("method must have a compatible result: "+y+", "+method.getName());

        this.method=method;
        this.constructor=null;
    }
    
    /** @param constructor : public Y(X) */
    private Converter(Constructor<Y> constructor, Class<X> x) throws IllegalArgumentException
    {
        if (constructor==null)
            throw new IllegalArgumentException("constructor can't be null");

        int modifiers = constructor.getModifiers();

        if (!Modifier.isPublic(modifiers))
            throw new IllegalArgumentException("constructor must be public: "+constructor.getName());

        if (constructor.getParameterTypes().length!=1)
            throw new IllegalArgumentException("constructor must take one and only one argument: "+constructor.getName());

        if (!constructor.getParameterTypes()[0].isAssignableFrom(x))
            throw new IllegalArgumentException("constructor must have a compatible argument: "+x+", "+constructor.getName());

        this.method=null;
        this.constructor=constructor;
    }
    
    @SuppressWarnings("unchecked")
    private Y parse(X x) throws RuntimeException
    {
        try 
        {
            return method==null ? constructor.newInstance(x) : (Y) method.invoke(null, x);
        }
        catch (IllegalAccessException e)
        {
            throw new RuntimeException(method==null ? constructor.toGenericString() : method.toGenericString(),e);
        } 
        catch (IllegalArgumentException e) 
        {
            throw new RuntimeException(e);
        } 
        catch (InvocationTargetException e)
        {
            throw new RuntimeException(x.toString(),e.getTargetException());
        } 
        catch (InstantiationException e)
        {
            throw new RuntimeException(constructor.toGenericString(),e);
        }
    }
}

public static void main(String[] args) throws Exception
{
    Method parseInt = Integer.class.getMethod("parseInt", String.class);
    Converter<String, Integer> stringToInteger = new Converter<String, Integer>(int.class, parseInt, String.class);
    
    int n = stringToInteger.parse("123");
    System.out.println(n);
}

1 réponse

tksteph Messages postés 204 Date d'inscription   Statut Membre Dernière intervention   25
 
Je pense, (mais pas certain) que le Patron Visiteur pourrait faire l'affaire
0
KX Messages postés 16761 Date d'inscription   Statut Modérateur Dernière intervention   3 020
 
Merci pour ton aide, cependant utiliser le patron visiteur n'est possible que si on conçoit soi même les classes visité. Or l'intérêt de la réflexion est justement de pouvoir utiliser n'importe quelle méthode de n'importe quelle classe, dynamiquement, sous réserve qu'elle respecte certaines contraintes. Et c'est sur les contraintes de types que j'ai des problèmes.

En effet pour vérifier que ma méthode est bien de la forme "public static Y method(X)", je dois vérifier que le paramètre est de type X (ou sous classe de X), de même que je dois vérifier que le type de retour est de type Y (ou super classe de Y). Or, les types X et Y je suis obligé de les passer en paramètres de la méthode alors qu'ils sont déjà spécifiés avec la généricité, je trouve ça idiot...

Quand dans mon main je fais "new Converter<String, Integer>(int.class, parseInt, String.class);" je précise deux fois les types de données, j'aimerais pouvoir alléger cette écriture en ne faisant que "new Converter<String, Integer>(parseInt);" en récupérant d'une manière ou d'une autre mes deux paramètres Class<X> x, et Class<Y> y, mais sans avoir à les mettre moi même dans la méthode...

J'aurais donc idéalement un code comme ça :

private static class Converter<X,Y>
{
    private final Method method;
    private final Constructor<Y> constructor;

    private Converter(Method method) throws IllegalArgumentException
    {
        Class<X> x = ?;
        Class<Y> y = ?;
        ...
    }
    
    private Converter(Constructor<Y> constructor) throws IllegalArgumentException
    {
        Class<X> x = ?;
        ...
    }
}

Mais si tu as d'autres idées je prends ;-)
0