1. 泛型构造器:
1) 构造器也是方法,既然有泛型方法,那么构造器也可以定义为泛型方法,那么就变成了泛型构造器;
2) 由于构造器也是方法,因此反省构造器的定义和普通泛型方法的定义完全相同,例如:public <T> MyClass(T t) { ... }
3) 使用泛型构造器:
i. 和使用普通泛型方法一样没区别,一种是显式指定泛型参数,另一种是隐式推断,如果是显式指定则以显式指定的类型参数为准,如果传入的参数的类型和指定的类型实参不符,将会编译报错;
!典型示例:A a = new <String>A("lala"); // 菱形实参还是写在方法名之前
ii. 这里唯一需要特殊注明的就是,如果构造器是泛型构造器,同时该类也是一个泛型类的情况下应该如何使用泛型构造器:
a. 因为泛型构造器可以显式指定自己的类型参数(需要用到菱形,放在构造器之前),而泛型类自己的类型实参也需要指定(菱形放在构造器之后),这就同时出现了两个菱形了,这就会有一些小问题,具体用法再这里总结一下;
b. 这里使用的类是这样定义的:public A<T> { public <E> A(E e) { ... } }
b. 全指定,例如:A<String> a = new <Integer>A<String>(15); // 所有类型实参全部显式指定,Integer代表E,String代表T
c. 全隐藏,例如:A<String> a = new A<>(15); // 可以隐式推断出Integer是E,String是T
d. 半隐藏,例如:A<String> a = new A<String>(15); // 还是可以推断出E是Integer,而String是T
e. 上面的叫做半隐藏T,但是不能半隐藏E,例如:A<String> a = new <Integer>A<>(15); // 虽然也可以正确推断,但是这种语法不允许!!会直接编译报错!
!!因此这里麻烦的是需要稍微记忆一下,不能半隐藏E
!!平时使用的时候就使用其中一种,推荐是全隐藏,越简洁越好,就是用一种就不会增加记忆负担;
2. 泛型方法的重载问题:
1) 泛型方法的定义非常灵活,只要形式不同就能形成重载,例如List<T>、List<T extends Xxx>、List<? super Xxx>等都会在重载时当成不一样的类型,因此都能形成重载;
2) 但是重载泛型方法的时候不能产生歧义,如果产生歧义即使编译没错最后运行时也可能发生错误!
3) 典型例子:
public static <T> void copy(Collection<T> dest, Collection<? extends T> src);!这两个方法形成重载,并且编译没有问题;
public static <T> void copy(Collection<? super T> dest, Collection<T> src);
!但是如果在运行时这样调用:copy(new ArrayList<Number> ln, new ArrayList<Integer> li); // 由于上面两个重载版本都符合,因此不知道到底应该调用该哪个方法,因此产生了歧义,随之而来的就是抛出异常!!
!!所以千万不要做这种模棱两可的事情;
3. 泛型数组的问题:这里不解释具体原理了,反正也没人用,所以就杜绝使用泛型数组
1) Java严格地来说不支持泛型数组,像List<String>[] arr = new List<String>[10]; // 直接编译报错
2) 其次,用类型参数创建数组也不支持,例如在一个泛型类中,出现这么一句话:new T[15]; // 也会直接编译报错
3) 因此一定要小心这两种情况,也就是说编程的时候一定要杜绝上述两种情况!
4. ?下限的典型应用:
1) 首先介绍一个简单的例子:就拿Collections的API(copy方法)来说吧:
public static <T> void copy(Collection<T> dest, Collection<? extends T> src) {!没有问题,类型兼容;
for (T ele: src) {
dest.add(ele);
}
}
!!但如果现在要求该方法返回最后一个被复制的元素呢?可能你会这样改:
public static <T> T copy(Collection<T> dest, Collection<? extends T> src) {!!但是这样又会让src丢失类型信息:Integer last = copy(new ArrayList<Number>(), new ArrayList<Integer>()); // 由于返回值类型就是src的类型上限Number,而Number到Integer并不兼容,因此这里会抛出类型转换异常;
T last = null;
for (T ele: src) {
last = ele;
dest.add(ele);
}
return last;
}
!!除非你对返回值进行强制类型转换,但是既然用了泛型还要那么麻烦地转换类型那不是很吃亏吗?
!!随意这里用?的上限来解决:
public static <T> T copy(Collection<? super T> dest, Collection<T> src) {!!这样src的类型就是确定的类型了,因此last的类型也是确定了类型,就是和src的类型一样!
T last = null;
for (T ele: src) {
last = ele;
dest.add(ele);
}
return last;
}