《JavaSE学习笔记 - 泛型基础》学习笔记
本文是本人学习《JavaSE学习笔记 - 泛型基础》这篇文章的笔记,记录自己学习理解的过程,主要是作为备忘。建议大家直接看《JavaSE学习笔记 - 泛型基础》这篇文章原文后再来看本文。
泛型基础知识
- Java SE5加入的新特性
- 实现了参数化类型
- 编译期类型检查
我很喜欢参数化类型这种表达,清晰明了。可以类比一个普通方法参数的概念。一个方法中的参数用一个形参来表示,到具体使用的时候传入具体的实参才确定值。在泛型中,在编译时并未指定具体的类型,用类型形参表示,到具体使用泛型类的时候才指定具体的类型实参。可以把<T>中的T想象成一个方法中的形参,在使用泛型类的时候传入具体的类型,比如<String>,这个具体的类型String相当于传入方法中的值
参数化泛型类型可以理解为执行泛型类型调用,这个过程就像执行函数调用,但有所区别的是执行该调用是传递类型作为实参。
泛型方法
- 定义泛型方法的语法也很简单,只要将
<T>
这样的泛型描述置于方法的返回值之前就可以了。 - 定义泛型方法时所指定的泛型形参的可见性只限定在该泛型方法,就如方法中的局部变量一样,所以,就算泛型类有同名的类型形参,也是与泛型方法中的类型形参没有任何关系。
-
如果使用泛型方法可以取代将整个类泛型化,那么就应该只使用泛型方法,因为这样可以使得代码更清晰简单
静态方法无法访问泛型类的类型形参,如果需要将泛型特性应用到静态方法上,则必须使用泛型方法了
public class GenericClass<T> {
// error: Cannot make a static reference to the non-static type T
//! public static void staticMethod(T t) { }
}
public class GenericClass {
// ok
public static <T> T staticMethod(T item) { return item; }
}
如下代码,静态泛型方法与泛型类都有同名的类型形参T
,但编译器没有发出任何错误或警告。而非静态泛型方法,编译器却发出警告The type parameter T is hiding the type T
,表示非静态泛型方法的类型形参T把泛型类的类型形参隐藏起来。静态泛型方法本身无法访问泛型类类型形参,所以不会触发隐藏现象。
泛型方法中T和泛型类中的T是毫不相关的类型,建议用不同的名字区分,提高代码的可读性
public class GenericClass<T> {
// ok
public static <T> T staticMethod(T item) { return item; }
// warning: The type parameter T is hiding the type T
@SuppressWarnings("hiding")
public <T> T method(T item) { return item; }
}
泛型方法调用时显式的类型说明
public class ExplicitTypeSpecification {
private <K, V> Map<K, V> map() {
return new HashMap<K, V>();
}
private static <K, V> Map<K, V> static_map() {
return new HashMap<K, V>();
}
static void f(Map<String, List<Integer>> map) { }
void g(Map<String, List<Integer>> map) { }
public void nonStaticMethod() {
// The method g(Map<String,List<Integer>>) in the type ExplicitTypeSpecification
// is not applicable for the arguments (Map<Object,Object>)
//! g(map());
g(this.<String, List<Integer>>map());
}
public static void staticMethod() {
f(ExplicitTypeSpecification.<String, List<Integer>>static_map());
}
}
以上代码之所以要使用显示的类型声明时因为既没有传入的参数类确定类型,也没有一个声明的变量来接收map()方法的返回值来确定泛型。其实以上代码也可以这样写,利用返回值接收对象的类型自动类型推断,就是要多些一行代码,如下所示:
public void nonStaticMethod() {
Map<String, List<Integer>> map = map();
g(map);
}
public static void staticMethod() {
Map<String, List<Integer>> map = ExplicitTypeSpecification.static_map();
f(map);
}
类型推断
在调用泛型方法时,我们没有显式指明类型实参时,编译器会根据泛型方法所接收的参数类型或即将返回赋值给的目标对象类型来决定泛型方法类型形参的具体类型到底是什么
- 根据参数类型推断
- 根据返回值接收对象类型推断
- 若同种类型形参接收多个不同类型的泛型实参,会推断出这些泛型实参的共同父类
static <T> T f(T itemA, T itemB) {
return itemB;
}
static <T> List<T> list() {
return new ArrayList<T>();
}
public static void main(String[] args) {
// 通过方法的参数推断,由于和1.5分属Integer和Double类型,那么会推断出共同的父类,即Number
Number number = f(1, 1.5);
// 根据返回值接收目标类型推断
List<Integer> iList = g();
// 与上面的一样,这种语法只能在Java SE7及以上的版本才能使用
List<Double> dList = new ArrayList<>();
}
显示类型说明
类型推断只对赋值操作有效,其他时候并不起作用。如下所示代码create()方法未赋值,执行结果直接传给accept方法(),并不能够根据accept方法的接收类型自动推断出泛型的类型。
public class LimitsOfInference {
static void accept(Map<String, Integer> map) { }
static <K, V> Map<K, V> create() {
return new HashMap<K, V>();
}
public static void main(String[] args) {
// The method accept(Map<String,Integer>) in the type LimitsOfInference
// is not applicable for the arguments (Map<Object,Object>)
accept(create());
}
}
上面代码问题可以根据泛型的显示类型说明来解决
accept(LimitsOfInference .<String,Integer>create());