10、java泛型 | |
10.1 为什么要有泛型 | 1课时 |
10.2 使用泛型 | 1课时 |
10.3 泛型的几个重要应用(在集合中使用泛型,自定义泛型类,自定义泛型接口,自定义泛型方法) | 1课时 |
10.4 泛型在继承上的体现 | 1课时 |
10.5 通配符的使用 | 1课时 |
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import org.junit.Test;
/**
* 泛型的使用
* 1.jdk 5.0 新增的特性
*
* 2.主要内容:
* 2.1 在集合中使用泛型 (掌握)
* 2.2 如何自定义泛型类、泛型接口;泛型方法 (理解)
* 2.3 泛型在继承上的体现 (掌握)
* 2.4 通配符的使用 (掌握)
*
*/
public class GenericTest {
//*************关于自定义泛型类、泛型接口、泛型方法的使用********************
/*
* 自定义泛型类的使用:
* 1.在实例化泛型类时,可以指明泛型的参数类型
* 2.提供泛型类的子类时,可以指明泛型的参数类型。比如:SubOrder1类
*/
@Test
public void test4(){
Order<String> order = new Order<>();
order.setT("足球");
String interest = order.getT();
System.out.println(interest);
//************
//SubOrder是泛型类
SubOrder<Integer> order1 = new SubOrder();
order1.setT(123);
//************
//SubOrder1:不是泛型类
// SubOrder1<Integer> order2 = new SubOrder1<Integer>();//错误的
SubOrder1 order2 = new SubOrder1();
String s = order2.getT();
//测试泛型方法
Integer[] arr = new Integer[]{1,2,3,4};
List<Integer> list = new ArrayList<>();
List<Integer> myList = Order.fromArrayToList(arr, list);
System.out.println(myList);
}
//*************关于集合中使用泛型的情况********************
//在集合中使用泛型
//1.在java集合中,声明的接口或类已经声明泛型
//2.在实例化接口时,可以指明泛型的类型。
//3.不能使用基本数据类型,需要使用对应的包装类替换。
//4.一旦创建对象时,使用了泛型,则通过对象调用方法、属性时,如果方法或属性中使用了类的泛型,则此时泛型类型就是
//对象实例化时的类型。
//5.如果在实例化不指明泛型参数的类型,则默认为Object类型。
//体现二:
@Test
public void test3(){
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("Tom1",34);
map.put("Tom2",44);
map.put("Tom3",33);
map.put("Tom4",32);
// map.put(33, "Tom");
Set<Entry<String,Integer>> entrySet = map.entrySet();
Iterator<Entry<String,Integer>> iterator = entrySet.iterator();
while(iterator.hasNext()){
Entry<String,Integer> entry = iterator.next();
System.out.println(entry.getKey() + "--->" + entry.getValue());
}
}
//体现一:
@Test
public void test2(){
ArrayList<Integer> list = new ArrayList<>();//类型推断
// ArrayList<int> list1 = new ArrayList<>();
list.add(78);
list.add(88);
list.add(77);
list.add(66);
//不能向List中添加非Integer类型的数据
// list.add("AA");
//遍历方式一:
// for(Integer i : list){
// //不需要强转
// System.out.println(i);
// }
//遍历方式二:
Iterator<Integer> iterator = list.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
//在集合中使用泛型前
@Test
public void test1(){
List list = new ArrayList();
list.add(78);
list.add(77);
list.add(88);
list.add(66);
//1.问题一:类型不安全。
list.add("Tom");
for(Object o : list){
//2.问题二:要想使用成绩,必须要强转:繁琐。同时,可能出现ClassCastException的异常。
int score = (Integer)o;
System.out.println(score);
}
}
}
import java.util.List;
/**
* 自定义泛型类。
* 仿照着List去创建。
* 1.在类的声明后面,指明泛型参数。参数通常使用:T E K V
* 2.在类的属性、方法、构造器中,可以使用类的泛型参数。
*/
public class Order<T> {
String orderName;
int orderId;
T orderT;
public Order(){}
public Order(T orderT){
this.orderT = orderT;
}
//如下的三个方法不是泛型方法!
public T getT(){
return orderT;
}
public void setT(T t){
this.orderT = t;
}
public String toString(){
return "name = " + orderName + "id = " + orderId + ",t = " + orderT;
}
//注意点1:使用了类的泛型参数的方法,不能声明为static
// public static T info(){
// return orderT;
// }
//注意点2:不能在try-catch中使用泛型参数表示异常类型
public void show(){
// try{
// System.out.println(10/0);
// }catch(T t){
//
// }
}
//定义泛型方法
//泛型方法所属的类,可以不是泛型类。
public static <E> List<E> fromArrayToList(E[] arr,List<E> list){
for(E e : arr){
list.add(e);
}
return list;
}
}
public class SubOrder
public SubOrder(){
// arr = new T[10];//错误的
arr = (T[]) new Object[10];
}
}
import java.util.List;
public class SubOrder1 extends Order<String>{
public static <E> List<E> fromArrayToList(E[] arr,List<E> list){
return null;
}
}
- 解决元素存储的安全性问题
- 解决获取数据元素时,需要类型强转的问题
泛型,JDK1.5新加入的,解决数据类型的安全性问题,其主要原理是在类声明时通过一个标识表示类中某个属性的类型或者是某个方法的返回值及参数类型。这样在类声明或实例化时只要指定好需要的具体的类型即可。
Java泛型可以保证如果程序在编译时没有发出警告,运行时就不会产生ClassCastException异常。同时,代码更加简洁、健壮。
Comparable c = new Date();
System.out.println(c.compareTo("red"));
JDK 1.5 之前
Comparable<Date> c = new Date();
System.out.println(c.compareTo("red"));
JDK 1.5
体会:使用泛型的主要优点是能够在编译时而不是在运行时检测错误。
10-2 使用泛型
1.泛型的声明
interface List
其中,T,K,V不代表值,而是表示类型。这里使
用任意字母都可以。常用T表示,是Type的缩写。
2.泛型的实例化:
一定要在类名后面指定类型参数的值(类型)。如:
List<String> strList = new ArrayList<String>();
Iterator<Customer> iterator = customers.iterator();
T只能是类,不能用基本数据类型填充。但可以使用包装类填充
把一个集合中的内容限制为一个特定的数据类型,这就是generics背后的核心思想
10-3 泛型的几个重要应用
10.3.1 在集合中使用泛型
10.3.2 自定义泛型类
10.3.3 自定义泛型接口
10.3.4 自定义泛型方法
10.3.1 对于泛型类(含集合类)
-
对象实例化时不指定泛型的话,默认为:Object。
-
泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如<E1,E2,E3>
-
泛型类的构造器如下:
public GenericClass(){}。而如下是错误的: public GenericClass<E>(){}
4.从泛型类派生子类,泛型类型需具体化。
5.如果泛型类是一个接口或抽象类,则不可创建泛型类的对象。
6.静态方法中不能使用类的泛型
7.异常类不能是泛型的
8.加入集合中的对象类型必须与指定的泛型类型一致。
9.泛型不同的引用不能相互赋值。
>尽管在编译时ArrayList<String>和ArrayList<Integer>是两种类型,但
是,在运行时只有一个ArrayList被加载到JVM中。
10 . 泛型的指定中不能使用基本数据类型,可以使用包装类替换。
11 . 不能使用new E[]。但是可以:
E[] elements = (E[])new Object[capacity];
10.3.2 自定义泛型类
class Person<T>{
//使用T类型定义变量
private T info;
//使用T类型定义一般方法
public T getInfo(){
return info;
}
public void setInfo(T info){
this.info = info;
}
//使用T类型定义构造器
public Person(){}
public Person(T info){
this.info = info;
}
//static的方法中不能声明泛型
//public static void show(T t){
//}
//不能在try-catch中使用泛型定义
//try{}
//catch(MyException<T> ex){}
}
10.3.3 对于泛型方法
方法,也可以被泛型化,不管此时定义在其中的类是不是泛型类。在泛型方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。
泛型方法的格式:[访问权限] <泛型> 返回类型 方法名([泛型标识 参数名称]) 抛出的异常
public class DAO {
public <E> E get(int id, E e){
E result = null;
return result;
}}
static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
for (T o : a) {
c.add(o);
} }
public static void main(String[] args) {
Object[] ao = new Object[100];
Collection<Object> co = new ArrayList<Object>();
fromArrayToCollection(ao, co);
String[] sa = new String[20];
Collection<String> cs = new ArrayList<>();
fromArrayToCollection(sa, cs);
Collection<Double> cd = new ArrayList<>();
// 下面代码中T是Double类,但sa是String类型,编译错误。
// fromArrayToCollection(sa, cd);
// 下面代码中T是Object类型,sa是String类型,可以赋值成功。
fromArrayToCollection(sa, co); }
10-4 泛型在继承上的体现
请输出如下来两段代码有何不同
void printCollection(Collection c) {
Iterator i = c.iterator();
for (int k = 0; k < c.size(); k++) {
System.out.println(i.next());
} }
void printCollection(Collection<Object> c) {
for (Object e : c) {
System.out.println(e);
} }
如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,G <B>并不是G<A>的子类型!
比如:String是Object的子类,但是List
public void testGenericAndSubClass() {
Person[] persons = null;
Man[] mans = null;
// 而 Person[] 是 Man[] 的父类.
persons = mans;
Person p = mans[0];
// 在泛型的集合上
List<Person> personList = null;
List<Man> manList = null;
// personList = manList;(报错)
}
10-5 通配符的使用
-
使用类型通配符:?
比如:List> ,Map,?>
List<?>是List、List -
读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object。
-
写入list中的元素时,不行。因为我们不知道c的元素类型,我们不能向其中添加对象。
- 唯一的例外是null,它是所有类型的成员
将任意元素加入到其中不是类型安全的:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // 编译时错误
因为我们不知道c的元素类型,我们不能向其中添加对象。
add方法有类型参数E作为集合的元素类型。我们传给add的任何参数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去。
另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object
public static void main(String[] args) {
List<?> list = null;
list = new ArrayList<String>();
list = new ArrayList<Double>();
//list.add(3);
list.add(null);
List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
l1.add(“xxx");
l2.add(15);
read(l1);
read(l2); }
static void read(List<?> list){
for(Object o : list){
System.out.println(o);
} }
10.5.1 有限制的通配符
<?>
允许所有泛型的引用调用
举例:
<? extends Number> (无穷小 , Number]
只允许泛型为Number及Number子类的引用调用
<? super Number> [Number , 无穷大)
只允许泛型为Number及Number父类的引用调用
<? extends Comparable>
只允许泛型为实现Comparable接口的实现类的引用调用
public static void printCollection3(Collection<? extends Person> coll){
//Iterator只能用Iterator<?>或Iterator<? extends Person>.why?
Iterator<?> iterator = coll.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
} }
public static void printCollection4(Collection<? super Person> coll){
Iterator<?> iterator = coll.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
} }
范例:泛型应用
用户在设计类的时候往往会使用类的关联关系,例如,一个人中可以定义一个信息的属性,但是一个人可能有各种各样的信息(如联系方式、基本信息等),所以此信息属性的类型就可以通过泛型进行声明,然后只要设计相应的信息类即可。
GenericPerson.java
应用案例
import java.util.ArrayList;
import java.util.List;
//泛型类的应用场景
//DAO:data(base) access object
public class DAO<T> {
private List<T> list = new ArrayList<T>();
//增
public void add(T t){
list.add(t);
}
//删
public T remove(int index){
T t = list.remove(index);
return t;
}
//改
public void update(int index,T t){
list.set(index, t);
}
//查一条数据
public T getInstance(int index){
return list.get(index);
}
//查多条数据构成的集合
public List<T> getForList(){
return list;
}
}
public class Customer { int id; String name; String email; Date birth; }
public class Order
String orderName;
int orderId;
T orderT;
}
public class CustomerDAO extends DAO<Customer>{
}
public class OrderDAO extends DAO<Order>{
}
import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.List;
import org.junit.Test;
public class GenericTest {
/*
* 有限制条件的通配符的使用
*
* ? extends A:
* 举例:List<? extends A> 可以作为List<B>或List<A>的父类。其中B类是A类的子类。
*
* ? super A:
* 举例:List<? super A> 可以作为List<B>或List<A>的父类。其中B类是A类的父类。
*/
@Test
public void test4(){
List<?> list = null;
List<? extends Number> list1 = null;
List<? super Number> list2 = null;
List<Object> list3 = null;
List<Number> list4 = null;
List<Integer> list5 = null;
// list1 = list3;
list1 = list4;
list1 = list5;
list2 = list3;
list2 = list4;
// list2 = list5;
}
//通配符: ? 的使用
/*
* A类是B类的父类,则G<A>和G<B>公共的父类为:G<?>
*
*
*/
@Test
public void test3(){
List<Object> list1 = null;
List<String> list2 = null;
List<?> list3 = null;
list3 = list1;
list3 = list2;
method3(list1);
method3(list2);
//关于数据的读、写
list2 = new ArrayList<String>();
list3 = list2;
//写入:不允许向使用了通配符的集合中写入数据。除非可以写入null.
// list3.add(123);
// list3.add("AA");
list3.add(null);
//读取:可以从使用了通配符的集合中读取数据。读取的数据的类型默认为Object.
list2.add("AA");
Object obj = list3.get(1);
System.out.println(obj);
}
/*
* 泛型在继承上的体现:
* 1.如果A类是B类的父类,则 List<A>与List<B>没有子父类关系的!
* 拓展:则 G<A> 和 G<B>没有子父类关系!
*
* 2.如果A类是B类的父类,则A<G> 是 B<G>的父类
*/
@Test
public void test2(){
Object obj = new String("AA");//多态
obj = new Date();//多态
Object[] arr1 = null;
String[] arr2 = null;
arr1 = arr2;//多态
method(arr2);
//看如下的情况:
List<Object> list1 = null;
List<String> list2 = new ArrayList<String>();
// list1 = list2;//list1和list2没有子父类关系!
// String s = new Date();
method1(list1);
// method1(list2);//编译不通过
Collection<Object> coll = null;
coll = list1;//多态的
// coll = list2;//编译不通过
//反证法
//假设list1 = list2可以的。 list1.add(123); 相当于把123放进了list2.错误!
}
public void method3(List<?> list){
}
public void method2(List<String> list){
}
public void method1(List<Object> list){
}
public void method(Object[] arr){
Object obj = arr[0];
}
@Test
public void test1(){
CustomerDAO customerDAO = new CustomerDAO();
customerDAO.add(new Customer());
List<Customer> list = customerDAO.getForList();
//。。。。
OrderDAO dao = new OrderDAO();
List<Order> list2 = dao.getForList();
}
}