@Test
public void TypeInferenceTest() {
inference();
uncheckedAssignment();
explicit();
}
private void inference() {
ArrayList<String> strings = new ArrayList<>();
strings.add("abc");
String s = strings.get(0);
assertThat(s, is("abc"));
}
private void uncheckedAssignment() {
ArrayList<String> strings = new ArrayList();
strings.add("abc");
String s = strings.get(0);
assertThat(s, is("abc"));
}
private void explicit() {
ArrayList<String> strings = new ArrayList<String>();
strings.add("abc");
String s = strings.get(0);
assertThat(s, is("abc"));
}
上面的代码使用了三种方法来实例化列表:类型推断,开放泛型类型和封闭泛型类型。
但如果我使用JD-GUI反编译生成的字节码,结果就是:
@Test
public void TypeInferenceTest() {
inference();
uncheckedAssignment();
explicit();
}
private void inference() {
ArrayList strings = new ArrayList();
strings.add("abc");
String s = (String)strings.get(0);
Assert.assertThat(s, Matchers.is("abc"));
}
private void uncheckedAssignment() {
ArrayList strings = new ArrayList();
strings.add("abc");
String s = (String)strings.get(0);
Assert.assertThat(s, Matchers.is("abc"));
}
private void explicit() {
ArrayList strings = new ArrayList();
strings.add("abc");
String s = (String)strings.get(0);
Assert.assertThat(s, Matchers.is("abc"));
}
看起来他们生成了相同的bytebote。
那么我们何时以及为什么要使用类型推断而不是其他两种呢?
答案 0 :(得分:4)
其他人已经说明了为什么它生成相同的代码。所以,回答实际问题 - 简短的回答是更喜欢这个:
ArrayList<String> strings = new ArrayList<>();
因为它比这更简洁,同时传达了相同的含义(对人类和编译器):
ArrayList<String> strings = new ArrayList<String>();
但是避免不必要地引起这样的警告:
ArrayList<String> strings = new ArrayList();
至于更简洁,这个:
ArrayList<String> strings = new ArrayList<String>();
并不是那么糟糕,输入它并没有真正的伤害(毕竟,我们已经多年来一直在做这件事)。但如果你最终写下这样的话:
Map<String, Map<Widget<Spork>, Bar<Llama>>> myMap = new HashMap<>();
如果你必须输入两次这样做会变得更加乏味:
<String, Map<Widget<Spork>, Bar<Llama>>>
答案 1 :(得分:2)
类型推断只是实例化paremeterized类型的一种不必要的长方式的捷径。泛型仍然通过类型擦除实现,因此您的字节码只有原始类型。
答案 2 :(得分:2)
正如您的示例所示,Java泛型由type erasure实现,并且通用代码在运行时与非泛型代码没有区别。这使得泛型纯粹是一个编译时功能 - 为了开发人员的利益而增加了类型安全性。
对于您的示例,new ArrayList()
,new ArrayList<String>()
和new ArrayList<>()
之间确实没有区别 - 所有重要的是它被分配给的变量的类型。仍然不建议使用new ArrayList()
,因为它看起来像遗留代码并且可能会混淆其他开发人员。它将导致编译器/ IDE显示标准的“原始类型”警告,因此分散注意力。但至少对于一个无法构造的人来说就是这样。
但是,提供类型参数或使用类型推断对任何采用泛型参数的构造函数都很重要。考虑这个例子:
Collection<Integer> ints = Arrays.asList(1, 2, 3);
List<String> strings1 = new ArrayList(ints); // compiles!
List<String> strings2 = new ArrayList<String>(ints); // won't compile
List<String> strings3 = new ArrayList<>(ints); // won't compile
至于为什么要使用new ArrayList<>(ints)
而不是new ArrayList<String>(ints)
,那么它的详细程度较低,并且避免重复已分配给变量的已指定的泛型类型。