在Java中的这个静态函数中,为什么必须在第1行重复<K, V>
?
public static <K, V> HashMap<K, V> newInstance() {
return new HashMap<K, V>();
}
我理解为什么HashMap<K, V>
是必要的,因为该函数返回一个HashMap,其泛型类型K和V分别作为键和值。但是,为什么函数签名中需要第一个<K, V>
?
答案 0 :(得分:6)
要指示该方法是泛型方法,请使用/返回泛型类型。如果它们不存在,编译器将查找名为K的具体类型,以及另一个名为V的具体类型。
答案 1 :(得分:1)
因为函数newInstance
也是通用的,通用类型 K 和 V 会被转发到HashMap
。它意味着像这样使用:
HashMap<Integer, String> map = newInstance<Integer,String>();
答案 2 :(得分:1)
要添加到其他答案,删除所有类型都会在编译时丢失。但是,编译器首先检查验证,这就是为什么需要这些类型的原因。
以下是从另一个SO answer中删除后发生的事情的示例:
但是当使用泛型时,它们会被转换为编译时检查和执行时转换。所以这段代码:
List list = new ArrayList(); list.add( “你好”); String x = list.get(0);
编译成
List list = new ArrayList(); list.add( “你好”); String x =(String)list.get(0);
答案 3 :(得分: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<>();}}
可以类型推断为以下之一:
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行代码的顺序(这应该是无关紧要的),你可能会改变破坏构建的公共接口。太脆了!