2

在Java的这个静态函数中,为什么必须<K, V>在第1行重复?

public static <K, V> HashMap<K, V> newInstance() {
  return new HashMap<K, V>();
} 

我理解为什么HashMap<K, V>是必要的,因为该函数返回一个 HashMap,其中泛型类型 K 和 V 分别作为键和值。但是,为什么<K, V>函数签名中第一个是必需的?

4

4 回答 4

6

指示该方法是泛型方法,使用/返回泛型类型。如果它们不存在,编译器将查找一个名为 K 的具体类型,以及另一个名为 V 的具体类型。

于 2012-06-23T22:04:53.473 回答
1

因为该函数newInstance也是泛型的,泛型类型KV被转发到HashMap. 它的意思是这样使用:

HashMap<Integer, String> map = newInstance<Integer,String>();
于 2012-06-23T22:04:33.467 回答
1

要添加到其他答案,擦除所有类型在编译时都会丢失。但是,编译器首先检查验证,这就是为什么需要这些类型。

这是从另一个 SO答案中删除后发生的情况的示例:

但是当使用泛型时,它们会被转换为编译时检查和执行时强制转换。所以这段代码:

列表列表 = 新的 ArrayList(); list.add("嗨"); 字符串 x = list.get(0);

被编译成

列表列表 = 新的 ArrayList(); list.add("嗨"); 字符串 x = (String) list.get(0);

于 2012-06-23T22:10:49.377 回答
1

编译器可以合理地推断类型参数吗?

是的,在示例情况下 -static HashMap<K,V>newInstance(){return new HashMap<>();}显然是static < K extends Object , V extends Object > HashMap<K,V>newInstance()return new HashMap<K,V>();}.

但是,如果您的编译器推断出类型参数,那么即使您输入错误的类名,您的代码仍然可以编译。 static void setName ( Sting name )可能是错误的,但您的编译器会假设您的意思是<Sting extends Object> static void setName ( Sting name );通过运行时擦除的魔力相当于static void setName ( Object name ) ;.

如果方法不是静态的,那么推理就会出现问题。 class X { HashMap<K,V>newInstance(){return new HashMap<>();}}可以被类型推断为以下之一:

  1. class X <K extends Object , V extends Object> { HashMap<K,V>newInstance(){return new HashMap<>();}}
  2. class X <K extends Object > { < V extends Object > HashMap<K,V>newInstance(){return new HashMap<>();}}
  3. class X <V extends Object> { < K extends Object > HashMap<K,V>newInstance(){return new HashMap<>();}}
  4. class X { <K extends Object , V extends Object> HashMap<K,V>newInstance(){return new HashMap<>();}}

另外,如果类型参数是推断出来的,那么顺序是什么。当它们被明确说明时,顺序是显而易见的。解决推断类型参数的顺序问题的唯一(对我来说很明显)方法是它们在代码中声明的顺序。但是,如果您只是颠倒 2 行代码的顺序(这应该是无关紧要的),您可能会更改破坏构建的公共接口。这么脆!

于 2012-06-23T22:28:43.260 回答