【问题标题】:Impossible to obtain Type tokens from Class objects?无法从 Class 对象中获取 Type 标记?
【发布时间】:2019-05-20 14:12:26
【问题描述】:

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

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 中的类型不是真正的对象,因此无法以启用第二种方法的方式从类对象 kClazzvClazz 中获取类型标记 KV .但是,语法糖会很好。

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

【问题讨论】:

  • 在这一行 MyInterfaceconcreteInstance = MyClassWarehouse.retrieveObject(uniqueKey); retriveObject 方法是否返回一个由 MyInterface 分别使用您的类型标记实现的对象?
  • 应该的。 MyClassWarehouse 专用于存储实现MyInterface 的(各种)类的对象。但是,在检索对象时,我想确保类型一致性。
  • 笨拙的代码也使用原始类型,这有点笨拙。

标签: java generics syntactic-sugar


【解决方案1】:

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

所以,是的,最简单的方法是在两端指定 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&lt;?,?&gt; 可以是标准的Consumer&lt;? extends MyReciever&lt;?,?&gt;&gt;。)

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

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

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

【讨论】:

  • 我需要一点时间来解开你在这里所说的一切。我会回来的。
猜你喜欢
  • 1970-01-01
  • 2013-08-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-19
  • 2013-04-25
相关资源
最近更新 更多