Java 泛型(Generic)
1.为什么要有泛型(Generic)
1.没有使用功能泛型时
- 解决元素存储的安全性问题
在集合没有使用泛型的时候,任何类型都可以添加到集合中:类型不安全 - 解决获取数据元素时,需要类型强转的问题
读取出来的对象需要强转:繁琐且可能会出现ClassCastException
2.使用泛型时
- 只有指定类型才可以添加到集合中:类型安全
- 读取出来的对象不需要进行强制转换:便捷
3.泛型
- 泛型,JDK1.5新加,用来解决数据类型的安全性问题,其主要原理是在类声明时,通过一个标识表示类中的某个属性类型或者是某个方法的返回值及参数类型,这样在类声明或者实例化话只要指定好需要的具体的类型即可。
- Java泛型可以保证如果程序在编译的时候没有发出警告,运行时就不会发生ClassCastException异常,同时,使代码更加简洁、健壮。
2.泛型的使用
1.泛型的声明
- interface List and class TestGen<K,V>
注:其中,T,K,V不代表值,而是表示类型。这里使用任意字母都可以。常用T表示,是Type的缩写
2.泛型的实例化:
- 一定要在类名后面指定类型参数的值(类型)。如:
List<String> strList = new ArrayList<String>();
Iterator<Customer> iterator = customers.iterator();
- T只能是类,不能用基本数据类型填充。
3泛型的几个重要使用
1. 泛型类(含集合类)需要注意的点
- 对象实例化时不指定泛型,默认为:Object。
- 泛型不同的引用不能相互赋值。
- 加入集合中的对象类型必须与指定的泛型类型一致。
- 静态方法中不能使用类的泛型。
- 如果泛型类是一个接口或抽象类,则不可创建泛型类的对象。
- 不能在catch中使用泛型
- 从泛型类派生子类,泛型类型需具体化
2.在集合中使用泛型
- 核心思想:把一个集合中的内容限制为一个特定的数据类型。
- demo
public class TestGeneric {
//1.在集合中没有使用泛型的情况下
public static void main(String[] args) {
List list = new ArrayList();
list.add(89);
list.add(88);
list.add(60);
list.add(new String("AA"));
//1.没有使用泛型,任何Object及其子类的对象都可以添加进来
for (int i = 0; i < list.size(); i++) {
//2.强转为int型是,可能会报ClassCastException
int score = (Integer)list.get(i);
System.out.println(score);
}
}
//2.在集合中使用泛型
@Test
public void test() {
List<Integer> list = new ArrayList<Integer>();
list.add(89);
list.add(88);
list.add(60);
// list.add("String");报错
for (int i = 0; i < list.size(); i++) {
int score = list.get(i);
System.out.println(score);
}
Iterator<Integer> it = list.iterator();
while(it.hasNext()) {
System.out.println(it.next());
}
}
@Test
public void test2() {
Map<String,Integer> map = new HashMap<String,Integer>();
map.put("daf", 11);
map.put("DD", 99);
map.put("Hh", 92);
Set<Map.Entry<String, Integer>> set = map.entrySet();
for(Map.Entry<String, Integer> o : set) {
System.out.println(o.getKey()+"---->"+o.getValue());
}
}
}
3.自定义泛型类、泛型方法
- demo(如何自定义泛型类和泛型方法)
//自定义泛型类的使用
@Test
public void test3() {
//1.当实例化泛型类的对象是,指明泛型的类型。
//指明以后,对应的类中所有使用泛型的位置,都变为实例化中指定的泛型类型
//2.如果我们自定义了泛型类,但是我们在实例化没有使用,默认使用Object类的
Order<Boolean> order = new Order();
order.setT(true);
System.out.println(order.getT());//true
order.add();
List<Boolean> list = order.list;
System.out.println(list);
SubOrder o = new SubOrder();
List<Integer> list1 = o.list;
System.out.println(list1);
//当通过对象调用泛型方法时,指明泛型方法的类型
Integer i = order.getE(35);
Double d = order.getE(2.3);
Integer[] in = new Integer[] {1,2,3};
List<Integer> list2 = new ArrayList<>();
List<Integer> list3 = order.fromArrayToList(in, list2);
System.out.println(list3);
}
//自定义泛型类
public class Order<T> {
private String name;
private int orderId;
private T t;
List<T> list = new ArrayList<>();
public void add() {
list.add(t);
}
public T getT() {
return t;
}
public void setT(T t) {
this.t = t;
}
//声明泛型方法
public <E> E getE(E e) {
return e;
}
//实现数组到集合的复制
public <E> List<E> fromArrayToList(E[] e,List<E> list){
for(E e1 :e ) {
list.add(e1);
}
return list;
}
...
@Override
public String toString() {
return "Order [name=" + name + ", orderId=" + orderId + ", t=" + t + "]";
}
}
4.泛型类的继承
1.继承时的写法
//继承泛型类货值接口时,可以指明泛型的类型
//class SubOrder extends Order<Integer>{
//
//}
//也可以不指明,但子类也要用泛型
class SubOrder<T> extends Order<T>{
}
2.继承关系
如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的类或接口,G<B>并不是G<A>的子类型!
比如:String是Object的子类,但是List<String >并不是List<Object>的子类。
/*
* 若类A是类B的子类,那么List<A>就不是List<B>的子接口
*/
@Test
public void test4() {
Object obj = null;
String str = "AA";
Object[] obj1 = null;
String[] str1 = new String[] {"AA","BB","DD"};
obj1 = str1;
List<Object> list = null;
List<String> list1 =new ArrayList<String>();
// list = list1;
//假设 list = list1满足
//list.add(123)
//Sting str = list1.get(0);--->123//出现问题
}
5.泛型接口
泛型接口与泛型类的用法类似。
4.通配符
1.什么是通配符
-
使用类型通配符:?
比如:List<?> ,Map<?,?>
List<?>是List、List等各种泛型List的父类。 -
读取List<?>的对象list中的元素时,永远是安全的,因为不管list的真实类型是什么,它包含的都是Object。
-
写入list中的元素时,不行。因为我们不知道c的元素类型,我们不能向其中添加对象。
唯一的例外是null,它是所有类型的成员。 -
将任意元素加入到其中不是类型安全的:
Collection<?> c = new ArrayList();
c.add(new Object()); // 编译时错误 -
因为我们不知道c的元素类型,我们不能向其中添加对象。
add方法有类型参数E作为集合的元素类型。我们传给add的任何参数都必须是6一个未知类型的子类。因为我们不知道那是什么类型,所以我们无法传任何东西进去。
另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object
- demo
/*
* 通配符 ?
* List<A>、List<B>....List<Y>都是List<?>的子类
* ? extends A:可以存放A及其子类
* ? super A:可以存放A及其父类
*/
@Test
public void test6() {
List<?> list = null;
List<Object> list1 = new ArrayList<Object>();
List<String> list2 = new ArrayList<String>();
list = list1;
list = list2;
show(list1);
// show(list2);
show1(list2);
List<? extends Number> list3 = null;
List<Integer> list4 = null;
list3 = list4;
// list3 = list1;
List<? super Number> list5 = null;
list5 = list1;
}
public void show(Collection<Object> coll) {
}
public void show1(Collection<?> coll) {
}
5.泛型类的应用DAO(操作数据库)
- 设计通用类DAO
//DAO:database access object 数据库访问对象
public class DAO<T> {
public void add(T t) {
//....
}
public T get(int index) {
return null;
}
public List<T> getForList(int index){
return null;
}
public void delete(int index) {
}
}
- 使用需要操作的类继承与DAO并声明DAO的泛型类型
public class CustomerDAO extends DAO<Customer>{
}
- Test
public class TestCustomerDAO {
public static void main(String[] args) {
CustomerDAO c = new CustomerDAO();
c.add(new Customer());
c.delete(1);
c.get(0);
}
}