1.泛型的定义
- 早期Java是使用Object来代表任意类型的,但是向下转型有强转的问题 ,这样程序并不安全。
- 所有使用该泛型参数的地方都被统一化,保证类型一致。如果未指定具体类型,默认是Object类型。集合体系中的所有类都增加了泛型,泛型也主要用在集合。
2.泛型类
- 泛型类就是把泛型定义在类上 ,用户使用该类的时候,才把类型明确下来。这样的话,用户明确了什么类型,该类就代表着什么类型,用户在使用的时候就不用担心强转的问题,和运行时转换异常的问题了
public class ClassGenericity {
public static void main(String[] args) {
/** 创建ObjectTool对象并指定元素类型为String */
ObjectTool<String> stringTool = new ObjectTool<>();
stringTool.setObj("muse");
System.out.println(stringTool.getObj());
/** 创建ObjectTool对象并指定元素类型为Integer */
ObjectTool<Integer> integerTool = new ObjectTool<>();
// integerTool.setObj("muse"); // 编译报错
integerTool.setObj(10);
System.out.println(integerTool.getObj());
}
/**
* 构建可以存储任何类型对象的工具类
*/
static class ObjectTool<T> {
private T obj;
public T getObj() {
return obj;
}
public void setObj(T obj) {
this.obj = obj;
}
}
}
3.泛型方法
- 除了在类上使用泛型,我们可能就仅仅在某个方法上需要使用泛型,外界仅仅是关心该方法,不关心类其他的属性,这 样 的话,我们在整个类上定义泛型,未免就有些大题小作了。那么此时,我们可以采用泛型方法
public class MethodGenericity<T> {
public static void main(String[] args) {
//创建对象
ObjectTool tool = new ObjectTool();
/** 调用方法,传入的参数是什么类型,T就是什么类型 */
tool.show("hello");
tool.show(12);
tool.show(12.5f);
}
static class ObjectTool {
//定义泛型方法
public <T> void show(T t) {
System.out.println(t);
}
}
}
4.泛型类派生的子类
- 泛型类是拥有泛型这个特性的类,它本质上还是一个Java类,那么它就可以被继承或实现。这里分两种情况:
第一种:子类明确泛型类的类型参数变量,则在实现时需要按指定的对象进行创建。
第二种:子类不明确泛型类的类型参数变量,则可以按泛型规则进行创建多种类型的对象。
/**
* 泛型类派生出的子类
*
* @author muse
*/
public class SubclassGenericity {
public static void main(String[] args) {
// 测试第一种情况
Inter<String> i = new InterImpl1();
i.show("hello");
// 编译错误
// Inter<Integer> ii = new InterImpl1();
// ii.show(1);
// 第二种情况测试
Inter<String> iii = new InterImpl2();
iii.show("100");
Inter<Integer> ii = new InterImpl2();
ii.show(1);
}
}
/**
* 把泛型定义在接口上
*/
interface Inter<T> {
void show(T t);
}
/** 实现1:子类明确泛型类的类型参数变量 */
class InterImpl1 implements Inter<String> {
@Override
public void show(String s) {
System.out.println(s);
}
}
/** 实现2:子类不明确泛型类的类型参数变量,实现类也要定义出T的类型 */
class InterImpl2<T> implements Inter<T> {
@Override
public void show(T t) {
System.out.println(t);
}
}
5.类型通配符
- List< ? >表示元素类型未知的List,它可以匹配任何类型的元素。声明List< ? >list后,不能向集合中添加元素,因为无法确定集合的元素类型,唯一例外的是null
public class TypeWildcard2 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("aaa");
list.add("bbb");
print(list);
List<Integer> list2 = new ArrayList<>();
list2.add(111);
list2.add(222);
print(list2);
}
public static void print(List<?> list) {
// 因为无法确定集合的元素类型,所以不能向集合中添加元素,会报编译错误
// list.add(list.get(0));
// list.add(1);
// list.add(new Object());
// list.add("ccc");
list.add(null); // 唯一例外的是可以添加null
for (Object obj : list)
System.out.println(obj);
}
}
6.泛型的上限与下限
7.类型擦除
- 泛型是提供给javac编译器使用的,它可以作为类型的限制,让编译器在源代码级别上,挡住非法类型的数据。
- 但是在JDK1.5之前没有泛型的概念,为了能够与之前版本代码兼容,编译器编译完带有泛型的java程序后,生成的class字节码文件中将不再带有泛型信息,这个过程称之为“擦除”。
8.桥接方法
- 由于类型擦除,泛型类的实现类中明确了类型,则会导致实现类中的类型与泛型接口不一致,所以会自动生成桥接方法,通过桥接方法进行类型的强转进行实现
- 桥接方法是jdk1.5引入泛型后,为使java泛型方法生成的字节码与jdk1.5版本之前的字节码兼容由编译器自动生成的。可用 method.isBridge() 判断method是否是桥接方法。