关于新版本中的类型推导和泛型方法,文档中还给了一个稍微复杂一点的例子,我在这里贴出来,原理和上面的Serializable例子都是一样就不再赘述,想巩固的可以再看一下:
public class BoxDemo {
public static <U> void addBox(U u,
java.util.List<Box<U>> boxes) {
Box<U> box = new Box<>();
box.set(u);
boxes.add(box);
}
public static <U> void outputBoxes(java.util.List<Box<U>> boxes) {
int counter = 0;
for (Box<U> box: boxes) {
U boxContents = box.get();
System.out.println("Box #" + counter + " contains [" +
boxContents.toString() + "]");
counter++;
}
}
public static void main(String[] args) {
java.util.ArrayList<Box<Integer>> listOfIntegerBoxes =
new java.util.ArrayList<>();
BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes);
BoxDemo.addBox(Integer.valueOf(20), listOfIntegerBoxes);
BoxDemo.addBox(Integer.valueOf(30), listOfIntegerBoxes);
BoxDemo.outputBoxes(listOfIntegerBoxes);
}
}
上面这段代码输出为:
Box #0 contains [10]
Box #1 contains [20]
Box #2 contains [30]
提一下,泛型方法addBox重点就在于在新java版本中你不需要再在方法调用中进行显示的类型说明,像这样:
BoxDemo.<Integer>addBox(Integer.valueOf(10), listOfIntegerBoxes);
编译器能够从传入addBox中的参数自动推断出参数类型是Integer.
4.2 类型推导与泛型类和非泛型类的泛型构造器(Type Inference and Generic Constructors of Generic and Non-Generic Classes)
额...这个也许英语的更好断句一点:Type Inference and Generic Constructors of Generic and Non-Generic Classes
其实,泛型构造器并不是泛型类的专利品,非泛型类也完全可以有自己的泛型构造器,看一下这个例子:
class MyClass<X> {
<T> MyClass(T t) {
// ...
}
}
假如对 MyClass类做出下面这样的实例化:
new MyClass<Integer>("")
OK,这里我们显示地指出了MyClass的泛参类型X是Integer,而对于构造器,编译器根据传入的String对象("")推导出形式参数T是String,这个在java7版本之中已经实现了,在Java8中有了什么改进呢?在Java8之后,对于这种具有泛型构造器的泛型类的实例化我们可以这么写:
MyClass<Integer> myObject = new MyClass<>("");
对,还是这一对尖括号(<>),江湖人称diamond,这样我们的编译器就能够自动推导出形式参数X是Integer,T是String了。这个其实和我们一开始Map<String,String>的例子很像,只是多了个构造器的泛型化。
需要注意的是:类型推导只能根据调用的参数类型、目标类型(这个马上会讲到)和返回类型(如果有返回的话)进行推导,而不能根据程序后面的一些需求来进行推导。
4.3 目标类型(Target Type)
前文已经提到过,编译器能够根据目标类型进行类型推导。一个表达式的目标类型指的是一种编译器根据表达式出现的位置而需要的正确的数据类型。比如这个例子:
static <T> List<T> emptyList();
List<String> listOne = Collections.emptyList();
在这里,List<String>就是目标类型,因为这里需要的是List<String>,而Collections.emptyList()返回的是List<T>,所以这里编译器就推断T一定是String。这个在Java 7 和 8 中都OK。但是在java 7 中,在下面这种情况中就不能正常编译了:
void processStringList(List<String> stringList) {
// process stringList
}
processStringList(Collections.emptyList());
这个时候,java7就会给出这种错误提示:
//List<Object> cannot be converted to List<String>
原因:Collections.emptyList() 返回的是List<T> ,这里的T需要一个具体类型,但是因为不能从方法声明中推断出所需的是String,所以编译器就给T了一个Object的值,很明显,List<Object>不能转型到List<String>.所以在java7版本中你需要这样调用这个方法:
processStringList(Collections.<String>emptyList());