在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>
函数签名中第一个是必需的?
在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>
函数签名中第一个是必需的?
指示该方法是泛型方法,使用/返回泛型类型。如果它们不存在,编译器将查找一个名为 K 的具体类型,以及另一个名为 V 的具体类型。
因为该函数newInstance
也是泛型的,泛型类型K和V被转发到HashMap
. 它的意思是这样使用:
HashMap<Integer, String> map = newInstance<Integer,String>();
要添加到其他答案,擦除所有类型在编译时都会丢失。但是,编译器首先检查验证,这就是为什么需要这些类型。
这是从另一个 SO答案中删除后发生的情况的示例:
但是当使用泛型时,它们会被转换为编译时检查和执行时强制转换。所以这段代码:
列表列表 = 新的 ArrayList(); list.add("嗨"); 字符串 x = list.get(0);
被编译成
列表列表 = 新的 ArrayList(); list.add("嗨"); 字符串 x = (String) list.get(0);
编译器可以合理地推断类型参数吗?
是的,在示例情况下 -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<>();}}
可以被类型推断为以下之一:
class X <K extends Object , V extends Object> { HashMap<K,V>newInstance(){return new HashMap<>();}}
class X <K extends Object > { < V extends Object > HashMap<K,V>newInstance(){return new HashMap<>();}}
class X <V extends Object> { < K extends Object > HashMap<K,V>newInstance(){return new HashMap<>();}}
class X { <K extends Object , V extends Object> HashMap<K,V>newInstance(){return new HashMap<>();}}
另外,如果类型参数是推断出来的,那么顺序是什么。当它们被明确说明时,顺序是显而易见的。解决推断类型参数的顺序问题的唯一(对我来说很明显)方法是它们在代码中声明的顺序。但是,如果您只是颠倒 2 行代码的顺序(这应该是无关紧要的),您可能会更改破坏构建的公共接口。这么脆!