typesafe异构Map

eiee3dmh  于 2021-07-12  发布在  Java
关注(0)|答案(1)|浏览(320)

我还有一个问题是关于https://gafter.blogspot.com/2006/12/super-type-tokens.html 显示了类型安全的异构容器模式。

public class FavoritesClass {

    private final Map<Class<?>, Object> favorites = new HashMap<>();

    public <T> void setFavorite(Class<T> klass, T thing) {
        favorites.put(klass, thing);
    }

    public <T> T getFavorite(Class<T> klass) {
        return klass.cast(favorites.get(klass));
    }

    public static void main(String[] args) {
        FavoritesClass f = new FavoritesClass();
        f.setFavorite(String.class, "Java");
        f.setFavorite(Integer.class, 0xcafebabe);
        String s = f.getFavorite(String.class);
        int i = f.getFavorite(Integer.class);
        System.out.println(s);
        System.out.println(i);

        // you simply can't make a type token for a generic type because of erasure
        f.setFavorite(List<String>.class, Collections.emptyList());
    }
}

这个例子展示了如何将各种对象保存在
favorites Map 以类型安全的方式使用类型标记作为 Map 钥匙。
但由于擦除的原因,不可能使用泛型类型。
我尝试使用超类型标记方法扩展示例( TypeReference 类)在文章中建议,但我不能使其类型安全。

/**
 * References a generic type.
 *
 * @author crazybob@google.com (Bob Lee)
 */
public abstract class TypeReference<T> {

    private final Type type;
    private volatile Constructor<?> constructor;

    protected TypeReference() {
        Type superclass = getClass().getGenericSuperclass();
        if (superclass instanceof Class) {
            throw new RuntimeException("Missing type parameter.");
        }
        this.type = ((ParameterizedType) superclass).getActualTypeArguments()[0];
    }

    /**
     * Instantiates a new instance of {@code T} using the default, no-arg
     * constructor.
     */
    @SuppressWarnings("unchecked")
    public T newInstance()
            throws NoSuchMethodException, IllegalAccessException,
                   InvocationTargetException, InstantiationException {
        if (constructor == null) {
            Class<?> rawType = type instanceof Class<?>
                ? (Class<?>) type
                : (Class<?>) ((ParameterizedType) type).getRawType();
            constructor = rawType.getConstructor();
        }
        return (T) constructor.newInstance();
    }

    /**
     * Gets the referenced type.
     */
    public Type getType() {
        return this.type;
    }
}

我的扩展示例:

public class FavoritesTypeReference {

    private final Map<TypeReference<?>, Object> favorites = new HashMap<>();

    public <T> void setFavorite(TypeReference<T> typeReference, T thing) {
        favorites.put(typeReference, thing);
    }

    public <T> void setFavorite(Class<T> klass, T thing) {
        TypeReference<T> typeReference = new TypeReference<T>() {
        };
        favorites.put(typeReference, thing);
    }

    public <T> T getFavorite(Class<T> klass) {
        TypeReference<T> typeReference = new TypeReference<T>() {
        };
        return klass.cast(favorites.get(typeReference));
    }

    public <T> T getFavorite(TypeReference<T> typeReference) {
        Class<T> klass = (Class<T>) typeReference.getType();
        return klass.cast(favorites.get(typeReference));
    }

    public static void main(String[] args) {
        FavoritesTypeReference f = new FavoritesTypeReference();

        f.setFavorite(String.class, "Java");
        f.setFavorite(Integer.class, 0xcafebabe);
        String s = f.getFavorite(String.class);
        int i = f.getFavorite(Integer.class);
        System.out.println(s);
        System.out.println(i);

        // erasure kicks in here, so I can't do it
        // you simply can't make a type token for a generic type.
        // f.setFavorite(List<String>.class, Collections.emptyList());

        f.setFavorite(new TypeReference<List<String>>() {
        }, Arrays.asList("Java", "Kotlin"));
        List<String> favorite = f.getFavorite(new TypeReference<List<String>>() {
        });
        System.out.println(favorite);
    }
}

如你所见,我得做些什么 Class<T> klass = (Class<T>) typeReference.getType();getFavorite 方法。
有什么方法可以避免这样做吗?
或者我的解决方案是唯一的出路?

vsikbqxv

vsikbqxv1#

经过反复试验,我意识到 Map<Class<?>, Object> favorites 或者 Map<TypeReference<?>, Object> favorites .
最后一个例子是:

public class FavoritesTypeReference {

    private final Map<TypeReference<?>, Object> favorites = new HashMap<>();

    public <T> void setFavorite(TypeReference<T> typeReference, T thing) {
        favorites.put(typeReference, thing);
    }

    public <T> T getFavorite(TypeReference<T> typeReference) {
        if (typeReference.getType() instanceof Class) {
            Class<T> klass = (Class<T>) typeReference.getType();
            return klass.cast(favorites.get(typeReference));
        } else {
            Class<T> klass = (Class<T>) ((ParameterizedType) typeReference.getType()).getRawType();
            return klass.cast(favorites.get(typeReference));
        }
    }

    public static void main(String[] args) {
        FavoritesTypeReference f = new FavoritesTypeReference();

        TypeReference<String> stringTypeReference = new TypeReference<String>() {
        };
        TypeReference<Integer> integerTypeReference = new TypeReference<Integer>() {
        };

        f.setFavorite(stringTypeReference, "Java");
        f.setFavorite(integerTypeReference, 0xcafebabe);
        String s = f.getFavorite(stringTypeReference);
        System.out.println(s);
        int i = f.getFavorite(integerTypeReference);
        System.out.println(i);

        TypeReference<List<String>> listStringsTypeReference = new TypeReference<List<String>>() {
        };
        f.setFavorite(listStringsTypeReference, Arrays.asList("Java", "Kotlin"));
        List<String> listStrings = f.getFavorite(listStringsTypeReference);
        System.out.println(listStrings);
    }
}
``` `getFavorite` 方法需要区分类和参数化类型,强制转换是必要的。

相关问题