【问题标题】:Java Generics puzzle - Get the class/type of object of generic type so that you can ensure the type laterJava 泛型之谜 - 获取泛型类型对象的类/类型,以便以后可以确保类型
【发布时间】:2015-08-05 01:36:54
【问题描述】:

看看这个简短的课程。

它有什么作用?它是一个 HashMap,其中键是 Class 对象,值是 Hashmap,键和值的类型由 Class 对象定义。

假设你想要一个方法 add(K obj),这将是一个方便的方法。你为什么要强迫用户输入 add(Person.class, personInstance),当你也可以让他把实例本身传递给你时——代码应该能够获取对象的类并将实例放在 key 下对象的类。但我不能。 add(K obj) 便捷方法无法编译:

public class TypedHashmap extends HashMap<Class, HashMap> {

    public <K> HashMap<K, K> getHashMap(Class<K> type) {
        return (HashMap<K, K>) getOrDefault(type, new HashMap<K, K>());
    }

    public <K> void add(K obj, Class<K> type) {
        HashMap<K, K> toModify = getHashMap(type);
        toModify.put(obj, obj);
    }

    public <K> void add(K obj) {
        add(obj, obj.getClass());
    }
}

编译错误:

我可以看到 Java 不确定 obj.getClass() 类型。但我不明白为什么。是因为它不能确保该类实际上不是扩展 K 的东西吗?这里有什么问题?这可以以某种方式解决吗?

【问题讨论】:

  • 根本问题是Object.getClass()的返回类型
  • 您真的想要地图地图吗?还是类映射到该类的实例?
  • 推理困难。我最初想要一张 , Set> 的地图。但是,您无法从集合中获取特定对象,您只能询问它是否存在或将其删除。请参阅此处stackoverflow.com/a/18380755/1920149 带来的问题以及您应该使用哈希图作为 Set 的替代品的建议:stackoverflow.com/questions/7283338/…
  • 如果你打算这样做,不要扩展HashMap。扩展HashMap是一个里面有各种孔的设计。有人可以用他们想要的任何地图致电put。写一个包裹地图的类更合适。

标签: java generics


【解决方案1】:

泛型是确保类型安全的工具。

您设计 API 的方式不是到此级别的类型安全,这就是您收到此错误的原因。

public <K> void add(K obj) {
    add(obj, (Class<K>) obj.getClass());
}

这是明确表示您无法确保在此处输入安全的方法。或者只是做一个

public void add(Object obj) {
    add(obj, (Class<Object>) obj.getClass());
}

这将为您提供相同级别的类型安全(无)。

如果您想要一个更简洁的解决方案(在更简单的 add 函数中不进行强制转换),您也必须修改其他 add 函数。

public <K> void add(K obj, Class<? super K> type) {
    HashMap<K, K> toModify = getHashMap(type);
    toModify.put(obj, obj);
}

public void add(Object obj) {
    add(obj, obj.getClass());
}

但最后我相信你正在实施一种反模式

问题是这样的:

Number a = new Integer(3);
map.add(a);
map.getHashMap(Number.class).contains(a);

产生错误。 Number 映射中的整数不是。因为您的地图不支持子类型和继承。

Integer 对象添加到列表中。但除非我知道哪个

【讨论】:

  • 可能是真的。感觉有点老套,不正确。但我想要一个返回对象,它可以填充很多类型的对象,而不必为对象中的每种类型实现一个集合 - 这不够灵活,而且会很多不必要的编码。仍然不知道如何更好地解决它。但这可能是一个不同的问题。
  • 如果要存储不同类型的对象,可以使用Object 的集合。如何添加带有额外类型检查的 getter 相当简单。 (仍然可能是一种反模式。不要在您选择不使用的地方伪造类型安全......)
  • 天啊,多么简单的解决方案啊!谢谢你。因此,例如,我将创建一个迭代器并迭代跳过任何与我当前正在寻找的类型不匹配的对象,对吧?
  • 在执行类型检查时,您可以使用Class.isAssignableFrom(Class),它返回布尔值,指示是否可以将 cls 类型的对象分配给此类的对象
【解决方案2】:

您没有使您的类泛型,因此所有方法泛型都是独立的(每个 K 是不同的 ANY)。我想你想要类似的东西

public class TypedHashmap<K> extends HashMap<Class<K>, HashMap<K, Class<K>>> {
    public HashMap<K, Class<K>> getHashMap(Class<K> type) {
        return (HashMap<K, Class<K>>) getOrDefault(type, new HashMap<>());
    }

    public void add(K obj, Class<K> type) {
        HashMap<K, Class<K>> toModify = getHashMap(type);
        toModify.put(obj, type);
    }

    public void add(K obj) {
        add(obj, (Class<K>) obj.getClass());
    }
}

【讨论】:

  • 谢谢。但实际上,它是一个 HashMap, HashMap>。但是,我尝试了您的方法,但是通过此修改使值也是 K。它允许我创建该方法,但是将 .entrySet() 方法转换为返回 Set 而不是 Set 的方法。稍后需要进行一些类转换,以便我可以访问键值对。我不清楚其中的原因。
【解决方案3】:

就编译器而言,obj.getClass() 返回一个 Class&lt;?&gt; 类型的实例,而您没有带有签名的方法

void add(K obj, Class<?> type)

你有

void add(K obj, Class<K> type)

这很接近,但并不完全正确。

【讨论】:

  • 好的,但是如果我将其更改为 Class> 它根本对我没有帮助,因为然后我在 getHashmap 行上收到一个 Hashmap,?> 我无法插入 K 类型对象作为键或值。
  • 试试这个:add(obj, (Class&lt;K&gt;) obj.getClass()); 确实很丑,但会让编译器停止抱怨。
猜你喜欢
  • 2019-11-04
  • 1970-01-01
  • 2017-04-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多