(二) 自定义泛型结构
1. 泛型的声明
interface List<T> 和 class GenTest<K,V>其中,T,K,V不代表值,而是表示类型。这里使用任意字母都可以。常用T表示,是Type的缩写。
2. 泛型的实例化
一定要在类名后面指定类型参数的值(类型)。如:
List<String> strList = new ArrayList<String>();
Iterator<Customer> iterator = customers.iterator();
- T只能是类,不能用基本数据类型填充。但可以使用包装类填充
- 把一个集合中的内容限制为一个特定的数据类型,这就是generics背后
的核心思想
3. 自定义泛型结构:泛型类、泛型接口注意的地方
1. 泛型类可能有多个参数,此时应将多个参数一起放在尖括号内。比如:
<E1,E2,E3>
2. 泛型类的构造器如下:public GenericClass(){}。
而下面是错误的:public GenericClass<E>(){}
3. 实例化后,操作原来泛型位置的结构必须与指定的泛型类型一致。
4. 泛型不同的引用不能相互赋值。
>尽管在编译时ArrayList<String>和ArrayList<Integer>是两种类型,但是,在运行时只有
一个ArrayList被加载到JVM中。
5. 泛型如果不指定,将被擦除,泛型对应的类型均按照Object处理,但不等价
于Object。经验:泛型要使用一路都用。要不用,一路都不要用。
6. 如果泛型结构是一个接口或抽象类,则不可创建泛型类的对象。
7. jdk1.7,泛型的简化操作:ArrayList<Fruit> flist = new ArrayList<>();
8. 泛型的指定中不能使用基本数据类型,可以使用包装类替换。
@Test
public void test(){
// 使用时:类似于Object,不等同于Object
ArrayList list = new ArrayList();
// list.add(new Date()); 有风险
list.add("hello");
// 泛型擦除,编译时不会类型检查
testArray(list);
// ArrayList<Object> list2 = new ArrayList<Object>();
// testArray(list2); 一旦指定了Object,编译时就会检查,必须按照Object来处理
}
public static void testArray(ArrayList<String> list) {
String str = "";
for (String s :list) {
str += s + ",";
}
System.out.println("元素:" + str);
}
9. 在类/接口上声明的泛型,在本类或本接口中即代表某种类型,可以作为非静态属性的类型、非静态方法的参数类型、非静态方法的返回值类型。但在静态方法中不能使用类的泛型。
10. 异常类不能是泛型的
11. 不能使用new E[]。但是可以:E[] elements = (E[])new Object[capacity];
参考:ArrayList源码中声明:Object[] elementData,而非泛型参数类型数组。
12.父类有泛型,子类可以选择保留泛型也可以选择指定泛型类型:
- 子类不保留父类的泛型:按需实现
- 没有类型 擦除
- 具体类型
- 子类保留父类的泛型:泛型子类
- 全部保留
- 部分保留
结论:子类必须是“富二代”,子类除了指定或保留父类的泛型,还可以增加自己的泛型
public class Father<T1,T2> {}
/**
* 子类不保留父类的泛型
* 1) 没有类型擦除
*/
public class Son1 extends Father {
/**
* 等价于 public class Son1 extends Father<Object,Object> {}
*/
}
/**
* 2)具体类型
*/
public class Son2 extends Father<Integer,String> {}
/**
* 子类保留父类的泛型
* 1) 全部保留
*/
public class Son3<T1,T2> extends Father<T1,T2> {}
/**
* 2)部分保留
*/
public class Son4<T2> extends Father<Integer,String>{}
public class Father<T1,T2> {}
/**
* 子类不保留父类的泛型
* 1) 没有类型擦除
*/
public class Son1<A,B> extends Father {
/**
* 等价于 public class Son1 extends Father<Object,Object> {}
*/
}
/**
* 2)具体类型
*/
public class Son2<A,B> extends Father<Integer,String> {}
/**
* 子类保留父类的泛型
* 1) 全部保留
*/
public class Son3<T1,T2,A,B> extends Father<T1,T2> {}
/**
* 2)部分保留
*/
public class Son4<T2,A,B> extends Father<Integer,String>{}
4. 自定义泛型
package com.liufei.test.myselfTest;
import java.util.ArrayList;
import java.util.List;
public class Order<T> {
String orderName;
int orderId;
/**
* 1. 类的内部构造就可以使用类的泛型
*/
T orderT;
public Order(){
// 编译不通过
// T[] ts = new T[10];
// 编译通过
T[] arr = (T[]) new Object[10];
}
public Order(String orderName,int orderId,T orderT) {
this.orderId = orderId;
this.orderName = orderName;
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 +
'}';
}
/**
* 2. 静态方法中不能使用泛型。
*/
/* public static void show(T orderT) {
System.out.println(orderT);
}*/
/**
* 3. 异常中不能使用泛型
*/
public void show(){
// 编译就不会通过
/* try {
} catch (T t) {
}*/
}
/**
* 泛型方法:在方法中出现了泛型的结构,泛型参数与类的泛型参数没有任何关系。
* 换句话说,泛型方法所属的类是不是泛型类都没有关系。
* 4. 泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定。
*/
public static <E> List<E> copyFromArrayToList(E[] arr) {
ArrayList<E> list = new ArrayList<E>();
if (arr != null) {
for (E e : arr) {
list.add(e);
}
}
return list;
}
}
package com.liufei.test.myselfTest;
import java.util.ArrayList;
import java.util.List;
public class SubOrder extends Order<Integer> { // SubOrder 不是泛型
public static <E> List<E> copyFromArrayToList(E[] arr){
ArrayList<E> list = new ArrayList<>();
if (arr != null) {
for(E e : arr){
list.add(e);
}
}
return list;
}
}
package com.liufei.test.myselfTest;
public class SubOrder1<T> extends Order<T> {
/**
* SubOrder1<T>:仍然是泛型类
*/
}
5. 泛型实例
package com.liufei.test.java;
import java.util.List;
public class DAO<T> {
/**
* 添加记录
* @param t
*/
public void add(T t) {
}
/**
* 删除记录
* @param t
* @return
*/
public boolean remove(T t) {
return false;
}
/**
* 修改记录
*/
public boolean update(T t) {
return false;
}
/**
* 查询一条记录
*/
public T getIndex(int index) {
return null;
}
/**
* 查询多条记录
* @param index
* @return
*/
public List<T> getForList(int index) {
return null;
}
// 泛型方法
// 举例:获取表中一共有多少条记录?获取最大的员工入职时间
public <E> E getValue(){
return null;
}
}
public class StudentDAO extends DAO<Student> {}
6. 泛型在继承上的使用
package com.liufei.test.java1;
import org.junit.Test;
import java.util.AbstractList;
import java.util.ArrayList;
import java.util.List;
/**
* 1. 泛型在继承方面的使用
*/
public class GenericTest {
/**
* 1.泛型在继承方面的体现
* 虽然类A是类B的父类,但是G<A> 和 G<B> 二者不具备 子父类关系,二者是并列关系。
* 补充:类A是类B的父类,A<G> 是 B<G> 的父类
*/
@Test
public void test1(){
Object obj = null;
String str = null;
obj = str;
Object[] arr1 = null;
Object[] arr2 = null;
arr1 = arr2;
// 编译不通过
/* Date date = new Date();
str = obj;*/
List<Object> list1 = null;
List<String> list2 = new ArrayList<>();
// 此时的list1和list2的类型不具有子父类关系。
// 编译不通过
// list1 = list2;
/*
反证法:
假设list1 = list2;
list1.add(123);导致混入非String的数据。出错。
*/
show(list1);
show1(list2);
}
public void show1(List<String> list) {
}
public void show(List<Object> list){
}
@Test
public void test2(){
AbstractList<String> list1 = null;
List<String> list2 = null;
ArrayList<String> list3 = null;
list1 = list3;
list2 = list3;
}
/**
* 2. 通配符的使用
* 通配符:?
* 类A是类B的父类,G<A>和G<B>是没有关系的,二者共同的父类是:G<?>
*/
@Test
public void test3(){
List<Object> list1 = null;
List<String> list2 = null;
List<?> list = null;
list = list1;
list = list2;
List<String> list3 = new ArrayList<>();
list3.add("AA");
list3.add("BB");
list3.add("CC");
list = list3;
// 添加(写入):对于List<?> 就不能向其内部添加数据了
// 除了添加 null 之外
// list.add("DDD"); // 编译不通过
list.add(null);
// 允许读取数据。读取的数据为Object
Object o = list.get(0);
System.out.println(o);
}
/**
*3.有限制条件的通配符的使用。
* ? extends A:
* G<? extends A> 可以作为G<A>和G<B>的父类,其中B是A的子类
*
* ? super A:
* G<? super A> 可以作为G<A>和G<B>的父类,其中B是A的父类
*/
@Test
public void test4(){
List<? extends Person> list1 = new ArrayList<>();
List<? super Person> list2 = new ArrayList<>();
List<Student> list3 = new ArrayList<>();
List<Person> list4 = new ArrayList<>();
List<Object> list5 = new ArrayList<>();
list1 = list3;
list1 = list4;
//list1 = list5; // 编译不通过
// list2 = list3; // 编译不通过
list2 = list4;
list2 = list5;
// 读取数据
Person person = list1.get(0);
// 编译不通过
// Student s = list1.get(0);
Object object = list2.get(0);
// 编译不通过
// Person p1 = list2.get(0);
// 写入数据
list2.add(new Person());
list2.add(new Student());
}
}
package com.liufei.test.java1;
public class Person {
}
package com.liufei.test.java1;
public class Student extends Person {
}
7.通配符
1.使用类型通配符:?
比如:List<?> ,Map<?,?>
List<?>是List<String>、List<Object>等各种泛型List的父类。
2.读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object。
3.写入list中的元素时,不行。因为我们不知道c的元素类型,我们不能向其中添加对象。
- 唯一的例外是null,它是所有类型的成员。
- 将任意元素加入到其中不是类型安全的:
Collection<?> c = new ArrayList<String>();c.add(new Object()); // 编译时错误因为我们不知道c的元素类型,我们不能向其中添加对象。add方法有类型参数E作为集合的元素类型。我们传给add的任何参数都必须是一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去
- 唯一的例外的是null,它是所有类型的成员。
- 另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object
通配符的使用
// 注意点1:编译错误:不能用在泛型方法声明上,返回值类型前面<> 不能使用?
public static <?> void test(ArrayList<?> list) {
}
// 注意点2:编译错误:不能用在泛型类声明上
class GenericTypeClass<?> {
}
// 注意点3:编译错误:不能用在创建对象上,右边属于创建集合对象
@Test
public void test5(){
// 编译错误
ArrayList<?> list = new ArrayList<?>();
// 编译通过
ArrayList<?> list2 = new ArrayList<>();
}