1.前言
使用
示例:
List<String> list = new ArrayList<String>();
//简写如下
List<String> list = new ArrayList<>();
2.深入泛型
定义泛型接口,类:
看Map类的源代码:
public interface Map<K,V>{
Set<K> keySet;
void put(K key, V value);
}
定义了泛型以后,可以用普通类型的地方,基本也可以用泛型.把泛型看成一种数据类型。
这样做的好处:只定义了一个接口,可以实现定义了无数接口的功能。
自定义泛型类,除了第一行要加一个以外,其他地方和普通的类似:
public class Apple<E>{
private E info;
public Apple(){};
public Apple(E info){
this.info = info
}
//getset方法省略
mian(){
Apple<String> a1 = new Apple<>("苹果");
Apple<Double> a2 = new Apple<>(123.123);
print(a1.getInfo());
}
子类继承泛型,或者实现接口:
类似于调用方法一样,子类不能还是泛型:
public class A extends Apple<String>{}
也可以不指定类型,去掉,这样默认为Object类型。
在带泛型的类或接口中,在静态方法,静态初始化块,静态变量的声明 和初始化中不允许使用泛型–类型形参。
错误示例:
sataic T info;
public static void bar(T msg){};
3.类型通配符
如果一个方法不能确定输入的时什么类型的变量,如何定义:
不加泛型也不好,会有警告
直接定义为Object类型不可以,因为List<Object>
不是List<String>
的父类,也不能包容,后者不能向上转型。
public void test(List<Object> c){}
test()
与之相对的是数组:Object[]
是String[]
的父类,可以包容,如:
Integer t = new Integer;
Number n =t;//不会报错;
类型通配符:
<?>
public void test(List<?> c){
for(Object o: c){
print(o);
}
}
这里的类型是Object,用起来还要转型。
设定类型通配符的上限:
public void draw(List<? extends shape> shapes){
for(shape s : shapes){
s.draw(this);
}
}
类似的,对接口和类的类型形参上限设定:
public class Apple<T extends Number>{}
4.泛型方法
///普通类或接口下的方法才可以用static和泛型
定义泛型方法
需求:把一个数组所有元素添加到集合中
- 定义泛型为Object,只能针对Object对象
static void fromArrayToCollection(Object[] arr, Collection<Object> c){
for(Object o: arr){
c.add(o);
}
}
- 使用
?
泛型呢,也不可以,因为不允许添加?
类型的数据到集合 - 泛型方法:
修饰符 <T, S> 返回类型 方法名(参数列表){}
static <T> void fromArrayToCollection(T[] arr, Collection<T> c){
for(T o: arr){
c.add(o);
}
}
//调用方法:直接调用
String[] as = new String[100];
Collection<Object> co = new ArrayList<>();
fromArrayToCollection(as,co);//T是Object,可以从下到上转型
//如果不是静态方法,通过对象调用,可以加上泛型,或者让编译器猜测
b.<String>fromArrayToCollection(as,co);//T是Object,可以从下到上转型
//保险起见,最好对T限制
static <T> void fromCollectionToCollection(Collection<? extends T> from, Collection<T> to){
泛型和通配符的区别
什么时候用泛型方法,什么时候用通配符?
大多时候可以用泛型代替类型通配符
boolean containsAll(Collection<?> c);
//等价于
<T>boolean containsAll(Collection<T> c);
//下面的T是指类或接口自带的泛型
boolean addAll(Collection< ? extends T> c);
//等价于
<E extends T>boolean addAll(Collection<E> c);
上面两个地方推荐通配符,因为泛型参数T只用了以一次。
泛型方法用来表示方法的一个或多个参数的依赖关系,或者返回值与参数之间的依赖关系,否则不用泛型方法。
总结:优先通配符
菱形语法和泛型构造器
class Foo{
public <T> Foo(T t){
print(t);
}
}
class Foo2<E>{
public <T> Foo2(T t){
print(t);
}
}
class Test{
main(){
new Foo(200);//Integer
Foo2<String> f0 = new Foo2<>(500);//隐式
Foo2<String> f1 = new <Integer> Foo2<String>(500);//显式,右边有Integer就要有String
设置通配符下限
<? super T>
比如上面的复制集合方法,复制Integer到Number,泛型是Number;
要在方法中返回最后一次复制的数,只能返回泛型Number,不是Integer。
返回Integer的改写如下
public static <T> T copy(Collection<? super T> dest, Collection<T> src) {
T last = null;
for(T o: src){
last = o;
dest.add(o);
}
return last;
}
这个copy方法和上一种copy方法不能重载,不能同时定义。
5. 擦除与转换
当把一个泛型变量复制给不带泛型变量时,会擦除泛型信息
class Apple<T extends Number>{
}
class Test{
main{
Apple<Integer> a = new Apple<>(6);//a的T是Integer
Apple b = a;//b的T是Number了
(String)b.getSize();//运行时报错,把Number转为String
泛型与数组
看不懂,感觉不重要,略