Java基础学习——第十二章 泛型
一、泛型的概念
1. 泛型的设计背景
集合容器类在声明阶段无法确定实际存储的是什么类型的对象,故JDK5.0之前只能把元素类型声明为Object
JDK1.5之后使用泛型来解决:由于此时除了元素的类型不确定之外,其他的部分都是确定的, 例如关于这个元素如何保存, 如何管理等。因此把元素的类型设计成一个参数 , 这种参数化类型 的概念就是泛型
Collection, List, ArrayList 中的就是泛型参数
2. 泛型的概念
泛型:在定义类、接口时,通过一个标识表示类中某个属性的类型、某个方法的返回值或形参类型、某个构造器的形参类型 。 这个泛型参数 将在使用时(例如:继承这个类或实现这个接口; 用这个类型声明变量、 创建对象时)确定(即传入实际的泛型参数, 也称为类型实参 )
3. 为什么要有泛型
解决集合中元素类型不安全 的问题:
集合中未使用泛型:任何类型的对象都可以添加到集合中(类型不安全的)
集合中使用泛型后:只有指定类型的对象才可以添加到集合中(类型安全的)
解决获取数据元素时,需要强制类型转换 的问题(强转可能会抛ClassCastException异常 )
集合中未使用泛型:读取出来的对象需要进行强制类型转换,还可能抛ClassCastException异常
集合中使用泛型后:读取出来的对象不需要进行强制类型转换
泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常
代码更加简洁、健壮
二、在集合中使用泛型
JDK5.0改写了集合框架中的全部接口和类,为这些接口、类增加了泛型支持,允许我们在实例化集合对象时再指定集合元素的类型
实例化集合对象时,在类名、接口名后声明一个类型实参"<指定类型>",显式指定集合元素的类型(泛型类型)
JDK7.0新特性——类型推断:ArrayList list = new ArrayList<>();
指定泛型类型后,在集合类或接口内部,凡是使用到泛型的位置(如:某个属性的类型、某个方法的返回值或形参类型、某个构造器的形参类型 )都会被指定为该泛型类型
如:add(E e) —> add(Integer e)
泛型类型必须是一个类,不能是基本数据类型,但可以是它们的包装类
如果实例化集合对象时,没有指明泛型类型(泛型参数的具体类型),则默认为java.lang.Object类型
import org. junit. Test ;
import java. util. * ;
public class GenericTest {
@Test
public void test1 ( ) {
ArrayList list = new ArrayList ( ) ;
list. add ( 78 ) ;
list. add ( 76 ) ;
list. add ( 89 ) ;
list. add ( 88 ) ;
list. add ( "Tom" ) ;
for ( Object score : list) {
int stuScore = ( Integer ) score;
System . out. println ( stuScore) ;
}
}
@Test
public void test2 ( ) {
ArrayList < Integer > list = new ArrayList < > ( ) ;
list. add ( 78 ) ;
list. add ( 87 ) ;
list. add ( 99 ) ;
list. add ( 88 ) ;
for ( Integer score : list) {
System . out. println ( score) ;
}
Iterator < Integer > iterator = list. iterator ( ) ;
while ( iterator. hasNext ( ) ) {
int stuScore = iterator. next ( ) ;
System . out. println ( stuScore) ;
}
}
@Test
public void test3 ( ) {
HashMap < String , Integer > map = new HashMap < > ( ) ;
map. put ( "Tom" , 87 ) ;
map. put ( "Jerry" , 87 ) ;
map. put ( "Jack" , 67 ) ;
Set < Map. Entry < String , Integer > > entries = map. entrySet ( ) ;
for ( Map. Entry < String , Integer > entry : entries) {
String key = entry. getKey ( ) ;
Integer value = entry. getValue ( ) ;
System . out. println ( key + "-->" + value) ;
}
Iterator < Map. Entry < String , Integer > > iterator = entries. iterator ( ) ;
while ( iterator. hasNext ( ) ) {
Map. Entry < String , Integer > entry = iterator. next ( ) ;
String key = entry. getKey ( ) ;
Integer value = entry. getValue ( ) ;
System . out. println ( key + "-->" + value) ;
}
}
}
练习:泛型在Java比较器中的应用
自然排序:在当前要比较的对象所在类实现Comparable接口时,指明其泛型类型(当前类),此时compareTo()方法的形参类型也被指定为该泛型类型,重写compareTo()方法时无需再进行instanceof判断和强制类型转换,也不用抛异常了
定制排序:在声明Comparator接口的实现类时,指明其泛型类型(需要比较的对象所在的类),此时compare()方法的形参类型也被指定为该泛型类型,则重写compare()方法时无需再进行instanceof判断和强制类型转换,也不用抛异常了
public class EmployeeTest {
@Test
public void test1 ( ) {
TreeSet < Employee > set = new TreeSet < > ( ) ;
set. add ( new Employee ( "liudehua" , 55 , new MyDate ( 1965 , 5 , 4 ) ) ) ;
set. add ( new Employee ( "zhangxueyou" , 43 , new MyDate ( 1987 , 5 , 4 ) ) ) ;
set. add ( new Employee ( "guofucheng" , 44 , new MyDate ( 1987 , 5 , 9 ) ) ) ;
set. add ( new Employee ( "liming" , 51 , new MyDate ( 1954 , 8 , 12 ) ) ) ;
set. add ( new Employee ( "liangchaowei" , 21 , new MyDate ( 1978 , 12 , 4 ) ) ) ;
Iterator < Employee > iterator = set. iterator ( ) ;
while ( iterator. hasNext ( ) ) {
Employee employee = iterator. next ( ) ;
System . out. println ( employee) ;
}
}
@Test
public void test2 ( ) {
TreeSet < Employee > set = new TreeSet < > ( new Comparator < Employee > ( ) {
@Override
public int compare ( Employee o1, Employee o2) {
MyDate b1 = o1. getBirthday ( ) ;
MyDate b2 = o2. getBirthday ( ) ;
return b1. compareTo ( b2) ;
}
} ) ;
set. add ( new Employee ( "liudehua" , 55 , new MyDate ( 1965 , 5 , 4 ) ) ) ;
set. add ( new Employee ( "zhangxueyou" , 43 , new MyDate ( 1987 , 5 , 4 ) ) ) ;
set. add ( new Employee ( "guofucheng" , 44 , new MyDate ( 1987 , 5 , 9 ) ) ) ;
set. add ( new Employee ( "liming" , 51 , new MyDate ( 1954 , 8 , 12 ) ) ) ;
set. add ( new Employee ( "liangchaowei" , 21 , new MyDate ( 1978 , 12 , 4 ) ) ) ;
Iterator < Employee > iterator = set. iterator ( ) ;
while ( iterator. hasNext ( ) ) {
System . out. println ( iterator. next ( ) ) ;
}
}
}
public class Employee implements Comparable < Employee > {
private String name;
private int age;
private MyDate birthday;
public Employee ( ) {
}
public Employee ( String name, int age, MyDate birthday) {
this . name = name;
this . age = age;
this . birthday = birthday;
}
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;
}
public MyDate getBirthday ( ) {
return birthday;
}
public void setBirthday ( MyDate birthday) {
this . birthday = birthday;
}
@Override
public String toString ( ) {
return "Employee{" +
"name='" + name + '\'' +
", age=" + age +
", birthday=" + birthday +
'}' ;
}
@Override
public int compareTo ( Employee o) {
return this . name. compareTo ( o. name) ;
}
}
public class MyDate implements Comparable < MyDate > {
private int year;
private int month;
private int day;
public MyDate ( ) {
}
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 sumYear = Integer . compare ( this . getYear ( ) , o. getYear ( ) ) ;
if ( sumYear != 0 ) {
return sumYear;
}
int sumMonth = Integer . compare ( this . getMonth ( ) , o. getMonth ( ) ) ;
if ( sumMonth != 0 ) {
return sumMonth;
}
return Integer . compare ( this . getDay ( ) , o. getDay ( ) ) ;
}
}
三、自定义泛型结构
1. 自定义泛型类 / 泛型接口
1.1 声明泛型类 / 泛型接口
public class HashMap < K , V > extends AbstractMap < K , V >
implements Map < K , V > , Cloneable , Serializable { }
public interface List < E > extends Collection < E > { }
将泛型的泛型参数 ,如T, E, K, V等,用尖括号括起来,放在类 / 接口的声明处(类名 / 接口名之后)
泛型类 / 泛型接口可能有多个泛型参数,此时应将多个泛型参数一起放在尖括号内
一旦指定类 / 接口的泛型,在类 / 接口的内部就可以将泛型参数作为非静态属性的类型、非静态方法的形参类型、非静态方法的返回值类型、类的构造器的形参类型 。但==静态方法中不能使用类的泛型参数 ==
类的泛型是在实例化类的对象时指定的,而静态结构是随着类的加载而加载的,生命周期不同
异常类(继承于Exception或RuntimeException)不能是泛型的
在继承泛型父类时,子类可以选择指定泛型类型,也可以选择保留泛型,还可以增加额外的泛型参数
子类不保留父类的泛型:按需实现
子类保留父类的泛型:泛型子类
class Father < T1, T2> {
}
class Son1 extends Father {
}
class Son2 extends Father < Integer , String > {
}
class Son3 < T1, T2> extends Father < T1, T2> {
}
class Son4 < T2> extends Father < Integer , T2> {
}
class Father < T1, T2> {
}
class Son < A , B > extends Father {
}
class Son2 < A , B > extends Father < Integer , String > {
}
class Son3 < T1, T2, A , B > extends Father < T1, T2> {
}
class Son4 < T2, A , B > extends Father < Integer , T2> {
}
1.2 实例化泛型类 / 泛型接口的泛型实现类
实例化泛型类时,在类名后声明一个类型实参"<指定类型>",显式指定泛型类型
JDK7.0新特性——类型推断:ArrayList list = new ArrayList<>();
指定泛型类型后,在泛型类内部,凡是使用到泛型参数的位置(如:某个属性的类型、某个方法的返回值或形参类型、某个构造器的形参类型 )都会被指定为该泛型类型
泛型类型必须是一个类,不能是基本数据类型,但可以是它们的包装类
如果实例化泛型类时,没有指明泛型类型(泛型参数的具体类型),则默认为java.lang.Object类型
泛型不同的引用类型变量不能相互赋值
对于泛型参数T,T[] arr = new T[10]; 是非法的;而 T[] arr = (T[]) new Object[10]; 是合法的
2. 自定义泛型方法
泛型方法:在方法声明时定义了独立于类的泛型参数。泛型方法的泛型参数与当前类是不是泛型类无关,与当前类的泛型参数也无关
泛型方法的格式:访问权限 <泛型标识> 返回类型 方法名(泛型标识 形参名) 抛出的异常 {}
public < E > List < E > copyFromArrayToList ( E [ ] arr) { }
在类的外部调用泛型方法时,泛型参数的类型一般会根据传入形参的类型进行推断
泛型方法可以声明为静态的:泛型方法的泛型参数是在调用方法时确定的,并非在实例化类的对象时确定 ,即==静态方法中不能使用类的泛型参数,但泛型方法可以是静态的 ==
import org. junit. Test ;
public class OrderTest {
@Test
public void test1 ( ) {
Order order = new Order ( ) ;
order. setOrderT ( 123 ) ;
order. setOrderT ( "ABC" ) ;
Order < String > stringOrder = new Order < > ( "orderAA" , 1001 , "order:AA" ) ;
stringOrder. setOrderT ( "ABC" ) ;
}
@Test
public void test2 ( ) {
SubOrder sub1 = new SubOrder ( ) ;
sub1. setOrderT ( 1122 ) ;
SubOrder1 < String > sub2 = new SubOrder1 < > ( ) ;
sub2. setOrderT ( "order2" ) ;
}
@Test
public void test3 ( ) {
Order < String > order = new Order < > ( ) ;
Integer [ ] arr = new Integer [ ] { 1 , 2 , 3 , 4 } ;
List < Integer > list = order. copyFromArrayToList ( arr) ;
System . out. println ( list) ;
}
}
public class Order < T > {
String orderName;
int orderId;
T orderT;
public Order ( String orderName, int orderId, T orderT) {
this . orderName = orderName;
this . orderId = orderId;
this . orderT = orderT;
}
public T getOrderT ( ) {
return orderT;
}
public void setOrderT ( T orderT) {
this . orderT = orderT;
}
@Override
public String toString ( ) {
return "Order{" +
"orderName='" + orderName + '\'' +
", orderId=" + orderId +
", orderT=" + orderT +
'}' ;
}
public static < E > List < E > copyFromArrayToList ( E [ ] arr) {
ArrayList < E > list = new ArrayList < > ( ) ;
for ( E e : arr) {
list. add ( e) ;
}
return list;
}
}
public class SubOrder extends Order < Integer > {
}
public class SubOrder1 < T > extends Order < T > {
}
3. 泛型类和泛型方法的使用情境
练习1
数据库不同表的增删改查操作:ORM思想(数据库中的一张表就对应Java中的一个类)
import org. junit. Test ;
public class DAOTest {
@Test
public void test1 ( ) {
CustomerDAO dao1 = new CustomerDAO ( ) ;
dao1. add ( new Customer ( ) ) ;
StudentDAO dao2 = new StudentDAO ( ) ;
dao2. add ( new Student ( ) ) ;
}
}
import java. util. List ;
public class DAO< T > {
public void add ( T t) {
}
public boolean remove ( int index) {
return false ;
}
public void update ( int index, T t) {
}
public T get ( int index) {
return null ;
}
public List < T > getForList ( int index) {
return null ;
}
public < E > E getValue ( ) {
return null ;
}
}
public class CustomerDAO extends DAO< Customer > { }
public class StudentDAO extends DAO< Student > { }
public class Customer { }
public class Student { }
练习2
定义一个泛型类 DAO,在其中定义一个 Map 成员变量,Map 的键为 String 类型,值为 T 类型。
分别创建以下方法:
public void save(String id,T entity): 保存 T 类型的对象到 Map 成员变量中
public T get(String id):从 map 中获取 id 对应的对象
public void update(String id,T entity):替换 map 中 key 为 id 的内容,改为 entity 对象
public List list():返回 map 中存放的所有 T 对象
public void delete(String id):删除指定 id 对象
定义一个 User 类: 该类包含:private 成员变量(int 类型)id,age;(String 类型)name
定义一个测试类: 创建 DAO 类的对象,分别调用其 save、get、update、list、delete方法来操作User对象,使用Junit单元测试
import java. util. * ;
public class DAO< T > {
private Map < String , T > map;
public DAO ( ) {
}
public DAO ( Map < String , T > map) {
this . map = map;
}
public void save ( String id, T entity) {
map. put ( id, entity) ;
}
public T get ( String id) {
return map. get ( id) ;
}
public void update ( String id, T entity) {
if ( map. containsKey ( id) ) {
map. put ( id, entity) ;
}
}
public List < T > list ( ) {
List < T > list = new ArrayList < > ( ) ;
Collection < T > coll = map. values ( ) ;
Iterator < T > iterator = coll. iterator ( ) ;
while ( iterator. hasNext ( ) ) {
T value = iterator. next ( ) ;
list. add ( value) ;
}
return list;
}
public void delete ( String id) {
map. remove ( id) ;
}
}
public class User {
private int id, age;
private String name;
public User ( ) {
}
public User ( int id, int age, String name) {
this . id = id;
this . age = age;
this . name = name;
}
public int getId ( ) {
return id;
}
public void setId ( int id) {
this . id = id;
}
public int getAge ( ) {
return age;
}
public void setAge ( int age) {
this . age = age;
}
public String getName ( ) {
return name;
}
public void setName ( String name) {
this . name = name;
}
@Override
public String toString ( ) {
return "User{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}' ;
}
@Override
public boolean equals ( Object o) {
if ( this == o) return true ;
if ( o == null || getClass ( ) != o. getClass ( ) ) return false ;
User user = ( User ) o;
if ( id != user. id) return false ;
if ( age != user. age) return false ;
return name != null ? name. equals ( user. name) : user. name == null ;
}
}
import org. junit. Test ;
import java. util. HashMap ;
import java. util. List ;
public class DAOTest {
@Test
public void test ( ) {
DAO1< User > dao = new DAO< > ( new HashMap < > ( ) ) ;
dao. save ( "1001" , new User ( 1001 , 34 , "周杰伦" ) ) ;
dao. save ( "1002" , new User ( 1002 , 20 , "昆凌" ) ) ;
dao. save ( "1003" , new User ( 1003 , 25 , "蔡依林" ) ) ;
dao. update ( "1001" , new User ( 1001 , 55 , "方文山" ) ) ;
dao. delete ( "1002" ) ;
List < User > list = dao. list ( ) ;
list. forEach ( System . out:: println ) ;
}
}
四、泛型在继承上的体现
如果B是A的子类或者子接口,而G是具有泛型声明的类或接口, G并不是G的子类型
泛型类型不同的引用不能相互赋值
比如: String是Object的子类,但是List并不是List的子类
如果B是A的子类、子接口,且都具有泛型声明;则B是A的子类型
泛型类型相同的子类/接口引用可以给父类/接口引用赋值
泛型类型相同的实现类引用可以给接口引用赋值
public class GenericExtendsTest {
@Test
public void test1 ( ) {
Object obj = null ;
String str = null ;
obj = str;
Object [ ] arr1 = null ;
String [ ] arr2 = null ;
List < Object > list1 = null ;
List < String > list2 = null ;
Collection < String > coll = null ;
List < String > list = null ;
ArrayList < String > arrList = null ;
coll = list;
list = arrList;
coll = arrList;
}
}
五、通配符:?
1. 通配符 ? 的使用:泛型类的公共父类
如果B是A的子类或者子接口,而G是具有泛型声明的类或接口, G并不是G的子类型,但二者有一个公共的父类:G>
如:List>是List、 List等各种泛型List的公共父类
public class GenericExtendsTest {
@Test
public void test2 ( ) {
List < Object > list1 = null ;
List < String > list2 = null ;
List < ? > list = null ;
list = list1;
list = list2;
}
}
2. 使用通配符后数据的读取和写入要求
读取List>的对象list中的元素是安全的 ,因为不管list的真实类型是什么,其元素总是Object类型的(多态)
往List>的对象list中写入元素是不允许的 ,因为并不知道该List>对象的元素类型 ,不能向其中添加对象
public class GenericExtendsTest {
@Test
public void test2 ( ) {
List < String > list3 = new ArrayList < > ( ) ;
list3. add ( "AA" ) ;
list3. add ( "BB" ) ;
list3. add ( "CC" ) ;
List < ? > list = list3;
Object o = list. get ( 0 ) ;
System . out. println ( o) ;
list. add ( null ) ;
}
public void print ( List < ? > list) {
Iterator < ? > iterator = list. iterator ( ) ;
while ( iterator. hasNext ( ) ) {
Object obj = iterator. next ( ) ;
System . out. println ( obj) ;
}
}
}
3. 有限制条件的通配符的使用
>
通配符指定上限
上限extends:使用时指定的类型必须是继承某个类,或者实现某个接口,即<=
通配符指定下限
下限super:使用时指定的类型不能小于操作的类,即>=
举例:
> (无穷小 , 无穷大)
extends Person> (无穷小 , Person]
只允许泛型为Person及Person子类的引用调用
super Person> [Person , 无穷大)
只允许泛型为Person及Person父类的引用调用
extends Comparable>
只允许泛型为Comparable接口的实现类的引用调用
@Test
public void test ( ) {
List < ? extends Person > list1 = null ;
List < ? super Person > list2 = null ;
List < Student > list3 = null ;
List < Person > list4 = null ;
List < Object > list5 = null ;
list1 = list3;
list1 = list4;
list2 = list4;
list2 = list5;
list1 = list3;
Person p = list1. get ( 0 ) ;
list2 = list4;
Object o = list2. get ( 0 ) ;
list2. add ( new Person ( ) ) ;
list2. add ( new Student ( ) ) ;
}