记录一下一些知识概念,用于自己遗忘时候的查缺补漏)
1.泛型概念
泛型的出现是为了使用在集合类里面,能够针对不同的数据类型,执行相同的代码。并且在编译期间就知道数据类型,便于编译器更好的提供帮助。同时为了兼容以前的版本,泛型有类型擦除的问题。
2. 泛型的分类
一般常用的泛型有泛型类,泛型接口,泛型方法。
2.1 泛型类
在一个类声明时候,就申明泛型类型。
public class Test1<T> {
private T t;
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
}
复制代码
2.2 泛型接口
在一个接口声明时候,就申明泛型类型。
public interface Test2<T> {
T getData(T t);
}
复制代码
2.3 泛型方法
方法申明时候,就声明泛型,并且在方法体中使用的方法,泛型方法,与是不是在泛型类中毫无关系。
public class Test3 {
public <T> T getData(T t) {
return t;
}
}
复制代码
3.通配符?和变换
通配符一般有三种,无界通配符,上界通配符和下界通配符。
3.1 无界通配符
采用 的形式,比如 List,无边界的通配符的主要作用就是让泛型能够接受未知类型的数据。 与T使用不同的是,T 是表示一个 确定的 类型,通常用于泛型类和泛型方法的定义, T 可以进行多重限定。使用& 符号设定多重边界。 ?是一个 不确定 的类型,通常用于泛型方法的调用代码和形参,不能用于定义类和泛型方法,使用有边界限定符可以定义泛型方法。 但是在下面这种情况下使用情况是一样的:
// 使用T,U两种的类型
public <T,U> void test1(List<T> dest, List<U> src){
}
// 通过? 来 表示两种不相关的数据类型
public void test2(List<?> dest, List<?> src){
}
//通配符是 不确定的,所以这个方法不能保证两个 List 具有相同的元素类型
public void test3(List<? extends Number> dest, List<? extends Number> src){
}
// 通过 T 来 确保 泛型参数的一致性
public <T extends Number> void test4(List<T> dest, List<T> src){
}
复制代码
3.2 上下界限的通配符
例如,表示上限<? extends Number>,接受Number的本身和子类,<? super Int>表示下界,接受Int 的本身和上层所有父类。
List<? extends Number> numbers = new ArrayList<Integer>();//协变
List<? super Integer> numbers2 = new ArrayList<Number>();//逆变
numbers.add(Integer.valueOf("5"));//error
Object o = numbers2.get(3);//取数据时候只能取出Object 顶层父类的数据,因为不确定此时的数据是哪个层级的父类。
复制代码
逆变和协变描述了具有继承关系的类型,通过类型构造器映射到另一范畴时所具有的继承关系。
保持原继承关系的为协变,继承关系反转的为逆变。