什么是泛型
- Java泛型(generics)是 JDK 5 中引入的一个新特性,在很大的程度上方便在集合上的使用。
- 泛型的本质是 参数化类型,最熟悉的就是定义方法的时候需要形参,调用方法的时候,需要传递实参。那”参数化类型”就是将原来具体的类型参数化
泛型目的
- 泛型的出现避免了在运行时强制类型转换而出现 ClassCastException (类型转化异常)。
泛型引出
请编写程序,在 ArrayList 中,添加 3 个 Dog 对象,Dog 对象含有 name 和 age,并输出 name 和age (要求使用getXxx())
传统方法
package com.jwt.generic; import java.util.ArrayList; public class Generic01 { public static void main(String[] args) { ArrayList arrayList = new ArrayList(); arrayList.add(new Dog("小黄",10)); arrayList.add(new Dog("小白",2)); arrayList.add(new Dog("小黑",5)); for (Object o : arrayList){ Dog dog = (Dog) o;//需要向下转型Object ->Dog System.out.println(dog.getName()); } } } class Dog { private String name; private int age; public Dog() { } public Dog(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
使用泛型
package com.jwt.generic; import java.util.ArrayList; public class Generic02 { public static void main(String[] args) { ArrayList<Dog> arrayList = new ArrayList<>(); arrayList.add(new Dog("小黄",10)); arrayList.add(new Dog("小白",2)); arrayList.add(new Dog("小黑",5)); for (Dog dog : arrayList){ System.out.println(dog.getName()); } } } class Dog { private String name; private int age; public Dog() { } public Dog(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
泛型优点
- 编译时,检查添加元素的类型,提高了安全性
- 不再提示编译警告
- 减少了类型转换的次数,提高效率
- 不使用泛型 Dog -> Object -> Dog //放入到 ArrayList 会先转成 Object,在取出时,还需要转换成 Dog
- 使用泛型 Dog-> Dog -> Dog //放入和取出时,不需要类型转换,提高效率
泛型使用
泛型有三种常用的使用方式:泛型类,泛型接口和泛型方法。
泛型类
class Person<E>{//如果创建对象时没有指定类型,默认是 Object E s; public Person(E s) { this.s = s; } }
- 泛型类型必须是引用类型(非基本数据类型)
List<Integer> list = new ArrayList<Integer>(); //正确 List<int> list2 = new ArrayList<int>();//错误
- 泛型的使用形式
List<Integer> list = new List<Integer>(); List<Integer> list = new List<>();//推荐使用简写
- 静态方法和静态属性不能使用泛型
- 泛型默认是Object
List List = new List(); //等价 List<Object> List = new List<>();
- 由于使用自定义泛型的类,只有在实例化的时候才知道这个类型参数是什么,所以导致
- (1)使用泛型的数组不能初始化;
- (2)静态方法和静态属性并不能使用泛型。
泛型接口
public interface Test_ <E>{ //... }
泛型接口的类型,在继承接口 或者 实现接口时确定。(默认Object)
例子
/** * 泛型接口的定义格式: 修饰符 interface 接口名<数据类型> {} */ public interface Inter<T> { public abstract void show(T t) ; } /** * 子类是泛型类 */ public class InterImpl<E> implements Inter<E> { @Override public void show(E t) { System.out.println(t); } } Inter<String> inter = new InterImpl<String>() ; inter.show("hello") ;
泛型方法
public <E> void Come(E e){ //... }
- 泛型方法,可以使用类声明的泛型,也可以使用自己声明泛型
class Fish<T, R> {//泛型类 public void run() {//普通方法 } public<U,M> void eat(U u, M m) {//泛型方法 } //说明 //1. 下面hi 方法不是泛型方法 //2. 是hi 方法使用了类声明的泛型 public void hi(T t) { } //泛型方法,可以使用类声明的泛型,也可以使用自己声明泛型 public<K> void hello(R r, K k) { System.out.println(r.getClass());//ArrayList System.out.println(k.getClass());//Float } }
例子
class Demo{ public <T> T fun(T t){ // 可以接收任意类型的数据 return t ; // 直接把参数返回 } }; public class GenericsDemo{ public static void main(String args[]){ Demo d = new Demo() ; // 实例化Demo对象 String str = d.fun("汤姆") ; // 传递字符串 int i = d.fun(30) ; // 传递数字,自动装箱 System.out.println(str) ; // 输出内容 System.out.println(i) ; // 输出内容 } };
源码中泛型使用
下面是List接口和ArrayList类的代码片段
//定义接口时指定了一个类型形参,该形参名为E public interface List<E> extends Collection<E> { //在该接口里,E可以作为类型使用 public E get(int index) {} public void add(E e) {} } //定义类时指定了一个类型形参,该形参名为E public class ArrayList<E> extends AbstractList<E> implements List<E> { //在该类里,E可以作为类型使用 public void set(E e) { ....................... } }
实例
- 定义Employee类
- 该类包含: private成员变量name,sal,birthday, 其中birthday为MyDate类的对象;
- 为每一个属性定义getter, setter方法;
- 重写toString方法输出name, sal, birthday
- 定义MyDate类
- 包含: private成员变量month,day,year;
- 并为每一个属性定义getter,setter方法;
- 创建该类的3个对象,并把这些对象放入ArrayList集合中(ArrayList需使用泛型来定义)
- 对集合中的元素进行排序,并遍历输出
- 排序方式:调用ArrayList 的sort方法,传入Comparator对象[使用泛型],先按照
name排序,如果name相同,则按生日日期的先后排序。 即:定制排序
Employee.java
package com.jwt.generic; public class Employee { private String name; private double sal; private MyDate birthday; public Employee(String name, double sal, MyDate birthday) { this.name = name; this.sal = sal; this.birthday = birthday; } public String getName() { return name; } public void setName(String name) { this.name = name; } public double getSal() { return sal; } public void setSal(double sal) { this.sal = sal; } public MyDate getBirthday() { return birthday; } public void setBirthday(MyDate birthday) { this.birthday = birthday; } @Override public String toString() { return "\nEmployee{" + "name='" + name + '\'' + ", sal=" + sal + ", birthday=" + birthday + '}'; } }
MyDate.java
package com.jwt.generic; class MyDate implements Comparable<MyDate> { private int year; private int month; private int day; public MyDate(int year, int month, int day) { this.year = year; this.month = month; this.day = day; } public int getYear() { return year; } public void setYear(int year) { this.year = year; } public int getMonth() { return month; } public void setMonth(int month) { this.month = month; } public int getDay() { return day; } public void setDay(int day) { this.day = day; } @Override public String toString() { return "MyDate{" + "year=" + year + ", month=" + month + ", day=" + day + '}'; } @Override public int compareTo(MyDate o) { int yearMinus = year - o.getYear(); if (yearMinus != 0) { return yearMinus; } //如果year相同,比较month int monthMinus = month - o.getMonth(); if (monthMinus != 0) { return monthMinus; } //如果year和month相同 return day - o.getDay(); } }
Main.java
package com.jwt.generic; import java.util.ArrayList; import java.util.Comparator; public class GenericExercise02 { public static void main(String[] args) { ArrayList<Employee> employees = new ArrayList<>(); employees.add(new Employee("小明",10000, new MyDate(2000,7,2))); employees.add(new Employee("小红",20000, new MyDate(2002,10,2))); employees.add(new Employee("小明",30000, new MyDate(2000,6,2))); System.out.println("employees=" + employees); employees.sort(new Comparator<Employee>() { @Override public int compare(Employee emp1, Employee emp2) { //先按照name排序,如果name相同,则按生日日期的先后排序。【即:定制排序】 //先对传入的参数进行验证 if (!(emp1 instanceof Employee && emp2 instanceof Employee)) { System.out.println("类型不正确.."); return 0; } //比较name int i = emp1.getName().compareTo(emp2.getName()); if (i != 0) { return i; } //封装后,将来可维护性和复用性,就大大增强. return emp1.getBirthday().compareTo(emp2.getBirthday()); } }); System.out.println("==对雇员进行排序=="); System.out.println(employees); } }
泛型的继承和通配符
- 泛型不具备继承性
//Object是String的父类 Object o = new String("xx");//正确 List<Object> list = new ArrayList <String>; //错误
- 通配符
:支持任意泛型类型
:支持A类以及A类的子类,规定了泛型的上限
:支持A类以及A类的父类,不限于直接父类,规定了泛型的下限
package com.jwt.generic; import java.util.ArrayList; import java.util.List; public class GenericExtends { public static void main(String[] args) { List<Object> list1 = new ArrayList<>(); List<String> list2 = new ArrayList<>(); List<AA> list3 = new ArrayList<>(); List<BB> list4 = new ArrayList<>(); List<CC> list5 = new ArrayList<>(); //如果是List<?> c ,可以接受任意的泛型类型 printCollection1(list1); printCollection1(list2); printCollection1(list3); printCollection1(list4); printCollection1(list5); //List<? extends AA> c: 表示上限,可以接受AA 或者AA 子类 printCollection2(list1);//× printCollection2(list2);//× printCollection2(list3);//√ printCollection2(list4);//√ printCollection2(list5);//√ //List<? super AA> c: 支持AA 类以及AA 类的父类,不限于直接父类 printCollection3(list1);//√ printCollection3(list2);//× printCollection3(list3);//√ printCollection3(list4);//× printCollection3(list5);//× } //说明: List<?> 表示任意的泛型类型都可以接受 public static void printCollection1(List<?> c) { for (Object object : c) { // 通配符,取出时,就是Object System.out.println(object); } } // ? extends ,可以接受AA 或者AA 子类 public static void printCollection2(List<? extends AA> c) { for (Object object : c) { System.out.println(object); } } // ? super,支持AA 类以及AA 类的父类,不限于直接父类, public static void printCollection3(List<? super AA> c) { for (Object object : c) { System.out.println(object); } } } class AA { } class BB extends AA { } class CC extends BB { }