1、为什么要有泛型
集合容器类在设计阶段/声明阶段不能确定这个容器到底实际存的是什么类型的 对象,所以在JDK1.5之 前只能把元素类型设计为Object,JDK1.5之后使用泛型来 解决。因为这个时候除了元素的类型不确定, 其他的部分是确定的,例如关于 这个元素如何保存,如何管理等是确定的,因此此时把元素的类型设计 成一个 参数,这个类型参数叫做泛型。 Collection, List,ArrayList 这个就 是类型参数,即泛型。
在集合中, Java加上泛型就可以让元素类型更安全,避免出现异常ClassCastException
1.1 什么是泛型
所谓泛型,就是允许在定义类、接口时通过一个标识表示类中某个属性的类 型或者是某个方法的返回值 及参数类型。这个类型参数将在使用时(例如, 继承或实现这个接口,用这个类型声明变量、创建对象 时)确定(即传入实 际的类型参数,也称为类型实参)。
2、使用泛型
我们在前面接触到泛型,都是用的是集合中的泛型,没有其余的地方使用到泛型
2.1 在集合中使用泛型
在集合中使用泛型
public void test1() {
List<String> list = new ArrayList<String>();
list.add("String"); // 安全类型
list.add(123); // 编译错误,类型不一致
}
在Map中使用泛型
public void test2() {
Map<String, Human> map = new HashMap<String, Human>();
map.put("String", new Huamn("Hello", 123));
map.put("String", new Obejct()); // 会发生错误,类型不一致,数据安全
}
3、泛型自定义
3.1 自定义泛型
3.1.1 泛型的声明
jdk8举例
// List
public interface List<E> extends Collection<E>{}
// Map
public interface Map<K,V> {}
说明,其中E, K, V 都是代表一个类型,不是具体类型,只要是变量就可以
3.1.2 泛型的实例化
在类名后面指定类型参数(泛型)
List<Sting> list=new ArrayList<String>();
Set<String> set=new HashSet();//1.7之后加的
说明, <> 一对尖括号中只能放置引用数据类型,非基本数据类型
3.2 定义泛型类/接口
3.2.1 特点说明和举例
泛型类可以有多个参数,放在同一个尖括号内,用", " 隔开
/*
L,U 类型参数
**/
public class Calculator<L,U> {
}
构造方法不含有尖括号
public class Order<E> {
List<E> productions = new ArrayList<>();
public void setProductions(List<E> productions) {
this.productions = productions;
}
public List<E> getProductions() {
return productions;
}
public Order() {
}
}
创建对象之后,泛型位置要和指定的泛型一致
public void test4() {
Order<Computer> order = new Order<>();
List<String> list = new ArrayList<>();
order.setProductions(list); // 这个位置会发生错误
}
泛型之间不能互相赋值
public void test5() {
Order<Computer> order=new Order<>();
Order<Human< order1=new Order<>();
//order=order1; 会发生错误
Order<Human> humanOrder = new Order<>();
Order<Student> studentOrder = new Order<>();
// humanOrder = studentOrder; // 会发生错误
Order<Student> studentOrder1 = new Order<>();
studentOrder1 = studentOrder; // 没有错误,赋值的时候,需要保持泛型一致
Human student = new Student();
}
若不指定泛型则会被擦除(不指定泛型,就会Object)
public void test6() {
List<Integer> list = new ArrayList<>();
list.add(123);
List llist1 = new ArrayList(); //
llist1.add(new Object()); // 如果不指定类型就会将泛型位置的类型变为Object llist1.add(new Human());// 向上转型
// Employee[] employees = new Employee[10];
// employees[0] = new GeneralEmployee(); // 向上转型,在泛型中也同样适用
}
泛型结构若是抽象的,则不可以创建对象(实例)
public void test7() {
Employee<Computer> employee = new Employee<Computer>() {
// new Employee<Computer>()
// 这个位置的Computer不能被省略
};
InterGeneric<Computer> interGeneric = new InterGeneric<Computer>() {};
// 匿名泛型类不可以省略泛型的内容,也就是不能使用菱形语法
// 但是可以完全省略泛型
}
简化操作<>
1.7 之后可以使用菱形语法
创建对象时,不能放基本数据类型
在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态 属性的类型、非静
态方法的参数类型、非静态方法的返回值类型。但在静态方法 中不能使用类的泛型。
// 泛型类或接口在使用静态方法时,不允许指定泛型
// 只有在创建对象时才可以指定泛型
异常不能带泛型
不能使用new E[]。但是可以: E[] elements = (E[])new Object[capacity]; Object[] elementData;
public List<E> getProductions() {
new E[123]; // 有错误
new E();// 有错误
E[] es = (E[])new Computer[123]; // 没有错误
E e = (E) new Computer(); // 没有错误
E e1 = (E) new Object(); // 没有错误
return productions;
}
父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
子类不保留父类的泛型:按需实现
没有类型 擦除
具体类型
子类保留父类的泛型:泛型子类
全部保留,要注意,保留的泛型写法上字母要和父类的一致
部分保留
结论:子类除了指定或保留父类的泛型,还可以增加自 己的泛型
3.3 定义泛型方法
单独一个方法也可以被泛型化,无论此时定义在其中的类是不是泛型类。在泛型方法中可以定义泛
型参数,此时,参数的类型就是传入数据的类型。
泛型方法的格式:
[访问权限] <泛型> 返回类型 方法名([泛型 参数名称]) 抛出的异常
举例
代码
public static <T> List<T> arrayToList(T[] array) {
List<T> list = new ArrayList<>();
for (T t : array) {
list.add(t);
}
return list;
}
4、继承
说明:泛型是泛型,类是类,泛型类是泛型类
@Test
public void test1() {
List<Human> humanList = new ArrayList<>();
List<Student> students = new ArrayList<>(); 5 /*
List<Human> 和List<Student>都是List, 这两个之前没有继承关系
humanList = students 是错误的 8 */
}
5、通配符
public void test2() {
List<?> list = new ArrayList<>();
List<Human> humanList = new ArrayList<>();
List<Student> students = new ArrayList<>();
list = humanList; // 正确 list = students; // 正确
// list.add(new Student()); 错误
// list.add(new Object()); 错误
int size = list.size();
System.out.println("size = " + size);
list.add(null); size = list.size();
System.out.println("size = " + size); }
public static void test(List<?> list) {
// List<?> 只读状态,不能插入元素,但可以插入null
// List<?> 其实就相当于List<任意一个类型>的父类了
}
public static void testExtends(List<? extends Person> list) {
Person human = list.get(0);
}
public static void testSuper(List<? super Person> list) {
Object object = list.get(0);
}
@Test
public void test2() {
List<? super Person> list = new ArrayList<>();
list.add(new Person());
list.add(new Student());
List<Human> humanList = new ArrayList<>();
humanList.add(new Human());
humanList.add(new Human());
humanList.add(new Human());
testSuper(humanList);
}
@Test
public void test1() {
List<? extends Human> list = new ArrayList<>();
// (-∞, Human]
list.add(null);
List<Person> personList = new ArrayList<>();
List<Student> studentList = new ArrayList<>();
testExtends(personList);
testExtends(studentList);
}