Voici donc une question un peu délicate (pour moi).

J'ai un objet générique. Appelez-le MyObject. Cet objet a une méthode qui retourne quelque chose du type T:

public class MyObject<T>
{
    private T _t;

    public MyObject(T t)
    {
        _t = t;
    }

    //...

    public T get()
    {
        return _t;
    }
}

(Évidemment, mon "MyObject" fait un peu plus mais c'est l'essentiel).

Maintenant, je veux avoir une carte de ce type:

Map<String, MyObject<?>> m = new HashMap<>();

Je veux pouvoir récupérer des cartes en utilisant un nom de chaîne prédéfini, et ces cartes peuvent être de n'importe quel MyObject. Par exemple, je pourrais appeler:

m.put("map_1", new MyObject<String>("String"));
m.put("map_2", new MyObject<Integer>(new Integer(3));
m.put("map_3", new MyObject<Long>(new Long(5));

Etc.

Mais - et voici la partie délicate - je veux que la carte "se souvienne" du type paramétré de MyObject lorsque je récupère une valeur de la carte. En utilisant

m.get("map_1");

Retournerait un

MyObject<Object> 

Type, puisque la carte a été définie comme contenant

MyObject<?> 

Valeurs. Donc:

m.get("map_1").get() // <-- This is an Object, not a String! 

Quelle modification (le cas échéant) est possible, afin de pouvoir obtenir les informations correctes - complètes - concernant l'objet MyObject récupéré, de sorte que l'invocation de la dernière ligne (m.get ("map_1")) renverrait un

MyObject<String>

Merci :)

Amir.

0
amirkr 20 avril 2017 à 02:44

3 réponses

Meilleure réponse

Les conteneurs hétérogènes Typesafe de Effective Java de Joshua Bloch pourraient fonctionner ici. En gros, vous ajoutez un objet Class pour représenter le type.

public class MyObject<T>
{
    private T _t;
    private Class<T> type;

    public MyObject( Class<T> type, T t)
    {
        _t = t;
        this.type = type;
    }

    //...

    public T get()
    {
        return _t;
    }

    public Class<T> getType() { return type; }
}

Ensuite, vous pouvez faire quelque chose comme ceci:

public <T> T get( Map<String, MyObject<?>> map, String key, Class<T> type ) {
   return type.cast( m.get( key ).get() );
}

Ce qui est sûr et compilera, mais générera une erreur d'exécution si vous vous trompez de type.

(Notez que je n'ai pas vraiment compilé cela, donc il se peut que des erreurs de syntaxe flottent. Mais la plupart des gens ne savent pas comment utiliser Class pour convertir des objets.)

4
markspace 20 avril 2017 à 00:03

Le système de types ne connaît que les types, pas les objets, et ne peut donc pas distinguer "key1" de "key2", car les deux sont de type String.

Si les clés ont des types différents, le moyen le plus simple est d'encapsuler une carte faiblement typée et d'utiliser des casts réfléchissants pour prouver au compilateur que les types sont corrects:

class Favorites {

    private Map<Class<?>,?> map = new HashMap<>();

    <V> V get(Class<V> clazz) {
        return clazz.cast(map.get(clazz));
    }

    <V> void put(Class<V> clazz, V value) {
        map.put(clazz, value);
    }
}

Favorites favs = new Favorites();
favs.put(String.class, "hello");
favs.put(Integer.class, 42);
favs.get(String.class).charAt(1); 
0
meriton 20 avril 2017 à 00:14

Vous pouvez obtenir la classe.

Class c = m.get("map_1").get().getClass();
if (String.class.equals(c)) {
    System.out.println("its a String");
}

Voici un test complet.

public class GenericsTest {

    /**
     * @param args the command line arguments
     */
    public static void main(String[] args) {

        Map<String, MyObject<?>> map = new HashMap<>();
        MyObject<String> obj = new MyObject<>("hello");

        map.put("greeting", obj);

        Class c = map.get("greeting").get().getClass();
        if (String.class.equals(c)) {
            System.out.println("its a String");
        }

    }

    static class MyObject<T> {

        T t;

        public MyObject(T t) {
            this.t = t;
        }

        T get() {
            return t;
        }

    }

}
0
Jose Martinez 19 avril 2017 à 23:57