第17章-1-泛型
概述
泛型(Generic),参数化类型
传统的方法调用时,传递的参数都是固定类型的,可以是基本类型或引用类型
使用泛型后,传递的参数类型可以不是固定的某种类型,只能是引用数据类型
语法上,使用<类型>作为泛型的语法词,其中类型多以一个单独大写字母表示,如T、E、K、V等
泛型在Java中,尤其是集合类中,应用非常广泛
擦除和补偿
- 泛型的出现提高了编译时的安全性,正因为编译时对添加的数据做了检查,则程序运行时才不会抛出类型转换异常。因此泛型本质上是编译时期的技术,是专门给编译器用的
- 加载类的时候,会将泛型擦除掉(擦除之后的类型为Object类型),这个称为泛型擦除法
- 为什么要有泛型擦除呢?其本质是为了让JDK1.4和JDK1.5能够兼容同一个类加载器。在JDK1.5版本中,程序编译时期会对集合添加的元素进行安全检查,如果检查完是安全的、没有错误的,那么就意味着添加的元素都属于同一种数据类型,则加载类时就可以把这个泛型擦除掉,将泛型擦除后的类型就是Object类,这样擦除之后的代码就与JDK1.4的代码一致
- 加载类的时候,会默认将类中的泛型擦除为Object类型,所以添加的元素就被转化为Object类型,同时取出的元素也默认为Object类型。而我们获得集合中的元素时,按理说取出的元素应该是Object类型,为什么取出的元素却是实际添加的元素类型呢?
- 这里又做了一个默认的操作,我们称之为泛型的补偿。在程序运行时,通过获取元素的实际类型进行强转,这就叫做泛型补偿(不必手动实现强制转换)。获得集合中的元素时,虚拟机会根据获得元素的实际类型进行向下转型,也就是会恢复获得元素的实际类型,因此我们就无需手动执行向下转型操作,从本质上避免了抛出类型转换异常
典型应用场景
如当前需要一个存储Integer类型数据的集合类,除了使用数组,可定义一个IntegerArrayList集合类,里面包装一个数组来实现
过一阵,需要一个存储Float类型数据的集合类,则需要重新定义一个FloatArrayList集合类
而且,可以陆续还有别的类型,如Student等自定义类型,那需要定义更多的集合类
此时,使用泛型,就可以针对上述多种类型,只定义一个泛型的ArrayList集合类
应用场景实例
应用实例1,传统方式定义的集合类,不推荐
集合类,只支持Integer类型
package com.bjpowernode.demo;
/**
* Integer类型集合类
*/
public class IntegerArrayList {
//保存元素的数组
private Integer[] datas;
//当前下标
private int index;
/**
* 无参构造,默认长度为10
*/
public IntegerArrayList() {
this(10);
}
/**
* 有参构造,初始化指定长度数组
*
* @param capacity
*/
public IntegerArrayList(int capacity) {
datas = new Integer[capacity];
}
/**
* 添加元素
*
* @param data
*/
public void add(Integer data) {
this.datas[this.index] = data;
this.index++;
}
/**
* 获取指定下标元素
* @param index
* @return
*/
public Integer get(int index) {
return this.datas[index];
}
/**
* 获取长度
*
* @return
*/
public int getLength() {
return this.index + 1;
}
}集合类测试
package com.bjpowernode.demo;
import java.util.ArrayList;
/**
* Integer类型的集合类测试,【只支持Integer类型】
*/
public class IntegerArrayListTest {
public static void main(String[] args) {
//定义Integer集合
IntegerArrayList integerArrayList = new IntegerArrayList();
//添加元素,只支持Integer类型
integerArrayList.add(3);
integerArrayList.add(5);
integerArrayList.add(7);
//获取元素
Integer result = integerArrayList.get(1);
System.out.println(result);
java.util.ArrayList a = new ArrayList();
}
}
应用实例2,泛型方式的集合类,推荐
集合类,支持所有引用类型
package com.bjpowernode.demo;
/**
* 通用集合类,使用泛型
*/
public class ArrayList<E> {
//保存元素的数组
private Object[] datas;
//当前下标
private int index;
/**
* 无参构造,默认容量为10
*/
public ArrayList() {
this(10);
}
/**
* 有参构造,指定容量
*
* @param capacity
*/
public ArrayList(int capacity) {
datas = new Object[capacity];
}
/**
* 添加元素
*
* @param data
*/
public void add(E data) {
this.datas[this.index] = data;
this.index++;
}
/**
* 获取指定下标元素
*
* @param index
* @return
*/
public E get(int index) {
return (E) this.datas[index];
}
/**
* 获取长度
*
* @return
*/
public int getLength() {
return this.index + 1;
}
}集合类测试
package com.bjpowernode.demo;
/**
* 泛型的集合类测试,【支持所有类型】
*/
public class ArrayListTest {
public static void main(String[] args) {
System.out.println("--------------泛型集合-Integer--------------");
//定义泛型集合,支持Integer
com.bjpowernode.demo.ArrayList<Integer> integerArrayList = new ArrayList<>();
//添加元素,只支持Integer类型
integerArrayList.add(3);
integerArrayList.add(5);
integerArrayList.add(7);
// integerArrayList.add(44.55); //报错,当前泛型对象只支持Integer
//获取元素
Integer integerResult = integerArrayList.get(1);
System.out.println(integerResult);
System.out.println("--------------泛型集合-Double--------------");
//定义泛型集合,支持Double
com.bjpowernode.demo.ArrayList<Double> doubleArrayList = new ArrayList <>();
//添加元素,只支持Double类型
doubleArrayList.add(1.1);
doubleArrayList.add(3.3);
doubleArrayList.add(5.5);
// doubleArrayList.add(77); //报错,当前泛型对象只支持Double
//获取元素
Double doubleResult = doubleArrayList.get(1);
System.out.println(doubleResult);
System.out.println("--------------泛型集合-其他类型...--------------");
}
}
泛型的定义与使用
泛型主要分类泛型类、泛型方法、泛形接口
Java类库中提供了大量的泛型类,如ArrayList、Map等
同样,也可以对泛型进行自定义
自定义泛型
泛型类
定义泛型类语法规则
访问修饰符 [其他修饰符] class 类名<泛型1, 泛型2, 泛型3, ...> {
}参考上述ArrayList,就是典型的自定义 泛型类的使用
泛型方法
定义泛型方法语法规则
访问修饰符 [其他修饰符] <泛型1, 泛型2, 泛型3, ...> 返回值类型 方法名(形参列表) {
}可以直接针对方法进行泛型定义
应用实例
应用实例1,泛型方法的定义和使用
泛型方法,冒泡排序
package com.bjpowernode.demo;
/**
* 泛型冒泡排序类
*/
public class BubbleSortUtil {
/**
* 泛形冒泡排序,支持任何实现了Comparable接口的元素数组
*/
public static <T extends Comparable> void sort(T[] datas) {
//外层循环,表示比较的轮次
for (int i = 0; i < datas.length; i++) {
//内层循环,每轮对数据进行比较,将大的(重的)沉下去
for (int j = 0; j < datas.length - (i + 1); j++) {
//如果当前位置的值大于后面位置的值,则替换,即往下沉
if (datas[j].compareTo(datas[j + 1]) > 0) {
T temp = datas[j + 1];
datas[j + 1] = datas[j];
datas[j] = temp;
}
}
}
}
}泛型方法测试
package com.bjpowernode.demo;
import java.util.Arrays;
public class BubbleSortUtilTest {
public static void main(String[] args) {
System.out.println("---------------Integer类型排序---------------");
Integer[] integerDatas = {33, 155, 77, 22, 66, 44, 111, 222, 133, 144};
System.out.println("【排序前】:" + Arrays.toString(integerDatas));
//排序
BubbleSortUtil.sort(integerDatas);
System.out.println("【排序后】:" + Arrays.toString(integerDatas));
System.out.println("---------------Double类型排序---------------");
Double[] doubleDatas = {99.99, 8.88, 33.33, 44.44, 55.55, 111.11, 77.77};
System.out.println("【排序前】:" + Arrays.toString(doubleDatas));
//排序
BubbleSortUtil.sort(doubleDatas);
System.out.println("【排序后】:" + Arrays.toString(doubleDatas));
System.out.println("---------------其他类型排序...元素需要实现Comparable接口---------------");
}
}
泛型接口
定义泛型接口语法规则
访问修饰符 [其他修饰符] interface 接口名<泛型1, 泛型2, 泛型3, ...> {
}实现接口的子类,可以是普通类,也可以是泛型类
应用实例
应用实例1,子类是普通类
泛型接口,获取最大值
package com.bjpowernode.demo;
/**
* 操作接口
*/
public interface IOperation<T> {
/**
* 最大值
*/
T max(T one, T two);
}普通子类,实现接口,支持Integer
package com.bjpowernode.demo;
/**
* Integer操作类
*/
public class IntegerOperate implements IOperation <Integer> {
@Override
public Integer max(Integer one, Integer two) {
return one > two ? one : two;
}
}普通子类,实现接口,支持Employee
Employee类
package com.bjpowernode.demo;
/**
* 员工类
*/
public class Employee {
private Integer id;
private String name;
private Character sex;
private Integer age;
private Boolean isLeader;
public Employee() {
}
public Employee(Integer id, String name, Character sex, Integer age, Boolean isLeader) {
this.id = id;
this.name = name;
this.sex = sex;
this.age = age;
this.isLeader = isLeader;
}
public Integer getId() {
return id;
}
public void setId(Integer id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Character getSex() {
return sex;
}
public void setSex(Character sex) {
this.sex = sex;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Boolean getLeader() {
return isLeader;
}
public void setLeader(Boolean leader) {
isLeader = leader;
}
@Override
public String toString() {
return "Employee{" + "id=" + id + ", name='" + name + '\'' + ", sex=" + sex + ", age=" + age + ", isLeader=" + isLeader + '}';
}
}普通子类
package com.bjpowernode.demo;
/**
* 员工操作类
*/
public class EmployeeOperate implements IOperation <Employee> {
@Override
public Employee max(Employee one, Employee two) {
return one.getAge() > two.getAge() ? one : two;
}
}普通子类测试类
package com.bjpowernode.demo;
public class OperationTest {
public static void main(String[] args) {
System.out.println("---------------Integer的最大值---------------");
Integer one = 33;
Integer two = 55;
IOperation<Integer> integerOperation = new IntegerOperate();
Integer integerMax = integerOperation.max(one,two);
System.out.println(integerMax);
System.out.println("---------------Employee的最大值---------------");
//员工1
Employee e1 = new Employee(11, "张三", '男', 25, true);
//员工2
Employee e2 = new Employee(13, "李四", '女', 27, false);
IOperation<Employee> employeeOperation = new EmployeeOperate();
Employee employeeMax = employeeOperation.max(e1,e2);
System.out.println(employeeMax);
}
}
应用实例2,子类是泛型类
泛型接口,获取最大值
package com.bjpowernode.demo;
/**
* 操作接口
*/
public interface IOperation<T> {
/**
* 最大值
*/
T max(T one, T two);
}泛型子类,针对实现Comparable接口元素的数组
package com.bjpowernode.demo;
import java.util.Comparator;
/**
* 泛型操作类,使用了Comparable接口,限定E的类型必须实现Comparable接口
*/
public class GenericOperation<E extends Comparable> implements IOperation <E> {
@Override
public E max(E one, E two) {
return one.compareTo(two) > 0 ? one : two;
}
}泛型子类测试类
odpackage com.bjpowernode.demo;
public class GenericOperationTest {
public static void main(String[] args) {
/*
注意:包装类都实现了Comparable接口
*/
System.out.println("---------------Integer的最大值---------------");
Integer one = 33;
Integer two = 55;
IOperation <Integer> integerOperation = new GenericOperation <>();
Integer integerMax = integerOperation.max(one, two);
System.out.println(integerMax);
System.out.println("---------------Double的最大值---------------");
Double first = 77.77;
Double second = 99.99;
IOperation <Double> doubleOperation = new GenericOperation <>();
Double doubleMax = doubleOperation.max(first, second);
System.out.println(doubleMax);
}
}
【练习】
- 练习应用实例内容,完成代码编写
边界【扩展】
- 泛型定义时,也可以对其适用的类型做一定的限制
- 主要通过extends和super两个关键字实现
- extends
- 表示泛型只能是指定类型或其后代类型,俗称上界
- 语法格式
- 用于泛型定义类型限定:
<T extends 类型>
,如:<T extends Comparable>
限制泛型T只能是Comparable类型或其后代类型 - 用于上界通配符,与通配符?结合使用
- 用于泛型定义类型限定:
- super
- 表示泛型只能是指定类型或其上辈类型,俗称下界
- 语法格式: <? super 类型>,与通配符?结合使用
通配符【扩展,了解】
使用?语法,表示任意泛型
并可与边界组合
分别为
- 无界通配符,格式为<?>,表示可以是任意泛型
- 上界通配符,格式为<? extends 类型>,表示只能是指定类型或其后代类
- 下界通配符,格式为<? super 类型>,表示只能是指定类型或其上前辈类
应用实例
具备继承关系的类
动物类
package com.bjpowernode.demo;
/**
* 动物类
*/
public class Animal {
private String name;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "Animal{" + "name='" + name + '\'' + '}';
}
}鸟类,动物类子类
package com.bjpowernode.demo;
/**
* 鸟类
*/
public class Bird extends Animal{
@Override
public String toString() {
return "Bird{} " + super.toString();
}
}狗类,动物类子类
package com.bjpowernode.demo;
/**
* 狗类
*/
public class Dog extends Animal {
@Override
public String toString() {
return "Dog{} " + super.toString();
}
}哈士奇类,狗类子类
package com.bjpowernode.demo;
/**
* 哈士奇类
*/
public class Husky extends Dog{
@Override
public String toString() {
return "Husky{} " + super.toString();
}
}泰迪类,狗类子类
package com.bjpowernode.demo;
/**
* 泰迪类
*/
public class Teddy extends Dog{
@Override
public String toString() {
return "Teddy{} " + super.toString();
}
}自定义集合类
package com.bjpowernode.demo;
/**
* 通用集合类,使用泛型
*/
public class ArrayList<E> {
//保存元素的数组
private Object[] datas;
//当前下标
private int index;
/**
* 无参构造,默认容量为10
*/
public ArrayList() {
this(10);
}
/**
* 有参构造,指定容量
*
* @param capacity
*/
public ArrayList(int capacity) {
datas = new Object[capacity];
}
/**
* 添加元素
*
* @param data
*/
public void add(E data) {
this.datas[this.index] = data;
this.index++;
}
/**
* 获取指定下标元素
*
* @param index
* @return
*/
public E get(int index) {
return (E) this.datas[index];
}
/**
* 获取长度
*
* @return
*/
public int getLength() {
return this.index + 1;
}
}通配符测试类
package com.bjpowernode.demo;
/**
* 通配符使用示例
*/
public class WildcardDemo {
public static void main(String[] args) {
//动物列表
com.bjpowernode.demo.ArrayList<Animal> animals = new ArrayList <>();
Dog dog = new Dog();
dog.setName("小花");
animals.add(dog);
Bird bird= new Bird();
bird.setName("八哥");
animals.add(bird);
//狗列表
com.bjpowernode.demo.ArrayList<Dog> dogs = new ArrayList <>();
dog = new Dog();
dog.setName("旺财");
dogs.add(dog);
Husky husky = new Husky();
husky.setName("二货");
dogs.add(husky);
//鸟列表
com.bjpowernode.demo.ArrayList<Bird> birds = new ArrayList <>();
bird= new Bird();
bird.setName("乌鸦");
birds.add(bird);
bird = new Bird();
bird.setName("喜鹊");
birds.add(bird);
//使用无界通配符
System.out.println("-------------------无界通配符-------------------");
WildcardDemo.showDatas(animals);
System.out.println("================");
WildcardDemo.showDatas(dogs);
System.out.println("================");
WildcardDemo.showDatas(birds);
//使用上界通配符
System.out.println("-------------------上界通配符-------------------");
// WildcardDemo.showDatasForExtends(animals); //报错,上界限定为Dog
WildcardDemo.showDatasForExtends(dogs);
// WildcardDemo.showDatasForExtends(birds); //报错,上界限定为Dog
//使用下界通配符
System.out.println("-------------------下界通配符-------------------");
WildcardDemo.showDatasForSuper(dogs);
System.out.println("================");
WildcardDemo.showDatasForSuper(animals);
// WildcardDemo.showDatasForSuper(bird); //报错,下界限定为Dog
}
/**
* 无限通配符
* @param datas
*/
public static void showDatas(ArrayList <?> datas) {
for (int index = 0; index < datas.getLength()-1; index++) {
System.out.println(datas.get(index));
}
}
/**
* 上界通配符
* @param datas
*/
public static void showDatasForExtends(ArrayList <? extends Dog> datas) {
for (int index = 0; index < datas.getLength()-1; index++) {
System.out.println(datas.get(index));
}
}
/**
* 下界通配符
* @param datas
*/
public static void showDatasForSuper(ArrayList <? super Dog> datas) {
for (int index = 0; index < datas.getLength()-1; index++) {
System.out.println(datas.get(index));
}
}
}
【练习】
- 练习应用实例内容,完成代码编写
实战和作业
- 【扩展】编写程序,要求如下,
- 模拟java.util.Stack类,实现一个自定义栈,能存放任意类型的数据,并提供入栈、出栈、栈中元素个数等方法
- 对自定义栈进行测试,分别使用一个包装类和一种自定义类型测试