首页 > 解决方案 > 无法从 Class 对象中获取 Type 标记?

问题描述

假设我有一个由各种类实现的通用接口。

public interface MyInterface<K, V> {
    // Some method declarations
    // ...
    // But owing to Java type erasure, we also have...
    Class<K> getKClass();
    Class<V> getVClass();
}

现在,我使用对象存储来获取实现MyInterface. 而且我在编写代码时不知道参数的类型KV. 但是,可以访问Class预先存储的相应对象。现在我可以在请求工厂时使用它们。

public MyInterface getObjectFromStore(String uniqueKey, 
      Class kClazz, Class vClazz) {
    MyInterface concreteInstance = MyClassWarehouse.retrieveObject(uniqueKey);

    // Perform type checks.
    boolean kMismatch = !kClazz.isAssignableFrom(concreteInstance.getKClass());
    boolean vMismatch = ...;
    // Mismatch handling...
    ...

    // Everything's okay
    return concreteInstance;
}

在我看来,如果我可以将我的方法编写为:

public <K, V> MyInterface<K, V> getObjectFromStore(String uniqueKey) {
    try {
        MyInterface<K, V> concreteInstance = MyClassWarehouse.retrieveObject(uniqueKey);
        return concreteInstance;
    } catch (ClassCastException cce) {
        // Better way to handle type mismatch
        ...
    }
}

但这要求我在编写代码时知道这些类。

MyInterface<String, Double> retrievedObject = getObjectFromStore("key123");

因为我只有类对象,所以我必须使用第一种“笨拙”的方法。尤其是:

// Must use this...
MyInterface retrievedObject = getObjectFromStore("key123", kClazz, vClazz);

// ... because this (or anything equivalent) is not possible.
MyInterface<kClazz, vClazz> retrievedObject = getObjectFromStore("key123");

在我看来,由于类型在 Java 中不是真正的对象,因此无法从类对象中获取类型标记K和以启用第二种方法的方式。但是,语法糖会很好。VkClazzvClazz

是否还有其他原因无法启用此功能?

标签: javagenericssyntactic-sugar

解决方案


如果您放弃编译时检查以支持运行时检查,那么您将需要来自两个来源的运行时类型对象才能进行任何比较。

所以,是的,最简单的就是Class在两端指定对象。在简单的情况下。

<T> void put(Class<T> t, T object);
<T> T get(Class<T> t);

添加特定的双参数化类型(Java 中没有更高类型的类型)、两种类型和一个键(对字符串编程的嘘声)。

<K,V> void put(Class<K> k, Class<V> v, Key key, My<K,V> object);
<K,V> My<K,V> get(Class<K> k, Class<V> v, Key key);

有机会将三个关键对象合二为一。

public final class MyKey<K,V> {
    public static of(Class<K> k, Class<V> v, SimpleKey key) {
        ....
    }
    ...
}
<K,V> void put(MyKey<K,V> key, My<K,V> object);
<K,V> My<K,V> get(MyKey<K,V> key);

或者,您可以寻找不同的类对象来源。它们可从方法签名中获得。

public interface MyReceiver<K,V> {
    void accept(My<K,V> object);
}
/** @param receiver Runtime type should specialise type parameters. */    
void get(MyReceiver<?,?> receiver); 

MyReciever<?,?>可能是一个标准Consumer<? extends MyReciever<?,?>>。)

您可以添加一个额外的简单键作为参数get(对 lambda 友好)、方法 on MyReceiver、注释 on acceptor MyReceiver,或者从中删除方法MyReceiver并使用它们运行时类型使用的任何方法名称(也许注释以查找您使用的方法)想)。

事实上,这种在接口中指定所需类型的方法比老式的异构映射更现代

(另请注意,“存储库”是您的仓库的标准术语。)


推荐阅读