在 Java 中,是不是可以创建一个类型安全的类映射到其类的实例?

Posted

技术标签:

【中文标题】在 Java 中,是不是可以创建一个类型安全的类映射到其类的实例?【英文标题】:In Java is it possible to create a type-safe Map of classes to instances of their class?在 Java 中,是否可以创建一个类型安全的类映射到其类的实例? 【发布时间】:2011-10-11 07:43:55 【问题描述】:

我想创建一个使用类作为键来返回该类的实例的映射。比如:

<T> Map< Class<T>, T > instanceMap = new HashMap< Class<T>, T > ();
instanceMap.put( Boolean.class, Boolean.TRUE );
instanceMap.put( String.class, "asdf" );   
instanceMap.put( Integer.class, 11 );

Boolean b = instanceMap.get( Boolean.class );
Integer i = instanceMap.get( Integer.class );
String s  = instanceMap.get( String.class  );

这可能吗?我有一种感觉,不,这不是因为我不能指出T 是一个泛型类型,而不是一个名为“T”的类。感觉有点像“高阶泛型”。

编辑:我知道我可以尝试扩展 Map 并实现我自己的包装器等,但我特别询问是否仅使用 Java 的通用支持来执行此操作。我对这个想法而不是这个特殊案例更感兴趣。

编辑 2: 如下所述,这是问题的重复(或更具体地说是子案例):Java map with values limited by key's type parameter。如果我能找到如此雄辩的措辞,我很可能会找到那个答案,而不是发布这个。

【问题讨论】:

每个类(键)只能存储一个值? @home 是的,这将被用作访问 DAO 的一种方式,其中每个 dao 类型只需要一个 dao。 JsonObject 可能是一个选项。您可以将任何您喜欢的值作为 jsonobject 的值。您需要的是包 org.json.JSONObject。但是,您可能无法将 foreach 与 JSONObject 一起使用,但有一些替代方法 ***.com/questions/9151619/… 如果你可以坚持任何你想要的,那么它就不是类型安全的。我特别希望将键的类型与值的类型联系起来。您的建议似乎与 Map&lt;String,Object&gt; 一样安全。 【参考方案1】:

你的意思是这样的?

public class Favorites 
  private Map<Class<?>, Object> favorites =
    new HashMap<Class<?>, Object>();

  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) 
    Favorites f = new Favorites();
    f.setFavorite(String.class, "Java");
    f.setFavorite(Integer.class, 0xcafebabe);
    String s = f.getFavorite(String.class);
    int i = f.getFavorite(Integer.class);
  

参考:Java map with values limited by key's type parameter

【讨论】:

我知道这可以用包装器来完成,我问的是使用纯泛型,但我假设你在我编辑之前回答了。 为了澄清我之前的评论,我对泛型类型系统的局限性更感兴趣,并制作了说明性示例来帮助构建问题。这就是为什么我没有接受你的很多赞成和其他合理的答案(即它回答了我要去的特定而不是一般)【参考方案2】:

据我了解,您是说在创建此地图后,您想用类似...的内容填充它

f.put(String.class, "Hello");
f.put(Integer.class, new Integer(42));
f.put(x.getClass(), x);

等等。对吧?

在这种情况下,我认为答案是否定的,你不能用泛型来做到这一点。泛型表示,对于给定的类实例——在本例中为映射——您正在指定适用的类型。因此,如果您说new HashMap&lt;String,Integer&gt;;,您是说针对此映射的所有操作都将使用一个字符串键和一个整数值。但这不是您在这种情况下想要做的。您希望能够将任何类型的对象放入类中,然后根据对象的类型限制可接受的键类型。这不是泛型的工作方式。它们不是彼此之间的关系,它们对于任何给定实例都是常数。

当然,您可以创建像new HashMap&lt;Class,Object&gt;; 这样的地图。这不会强制该类成为相应对象的类,但它允许您输入此类值。

除此之外,我认为您还需要一个包装器。我是否应该指出包装器的 put 只需要一个参数,因为它可以通过在其上执行 getClass() 来确定参数的类,不需要告诉它?

【讨论】:

【参考方案3】:

在您的示例中,T 对于每个键/值必须不同,而对于泛型,T 对于每个键/值必须相同。所以不,这是不可能使用泛型的。 (但当然可以通过强制转换和/或不使用泛型来实现)

【讨论】:

我也很怀疑,我只是想确保我没有遗漏什么。但是with generics, T must be the same for each key/value 听起来像是继承自 C++ 的限制,而不是 Java 泛型需要具备的限制;毕竟,在我的示例中,它仍然是静态可验证的。【参考方案4】:

Map&lt;T, U&gt; 的重点是将 T 映射到 U。 T 和 U 可以是任何类(假设没有通配符),但一旦确定,就不能更改。在您的情况下,您想要一个根据您使用的键返回不同类的映射,这显然是不同的。

解决这个问题的通常方法是制作 U Object,因为您可以继续将其转换为您需要的任何内容。但这当然破坏了泛型应该提供的类型安全性。

简而言之,如果不编写某种包装器,我认为这是不可能的。

【讨论】:

【参考方案5】:

T 在创建实例时必须设置为某种特定类型,因此您将无法使用put 方法将不同类型添加到地图中。另一方面,泛型方法在调用它们时会设置其类型参数,从而允许你做你想做的事情。

Guava 有一个 ClassToInstanceMap 类型,它使用泛型方法来处理将实例安全地放入地图并取出它们。

【讨论】:

以上是关于在 Java 中,是不是可以创建一个类型安全的类映射到其类的实例?的主要内容,如果未能解决你的问题,请参考以下文章

Java的类的创建

C++17好用的类

在C#的类库中可以创建WPF的窗口吗

Java泛型

Java中单例模式的安全性分析

c# 泛型学习