泛型详解之
为什么需要学习泛型?
- 在JDK1.5之前是没有泛型,当时集合能够存储的都是使用父类Object类型作为形参接受所有的类型
- 如果存储的是同一种类型向下转型没问题,如果集合存储的是不同类型,那么会出现ClassCastException类型转换异常
- 所以我们在转型之前做类型判断,判断后虽然解决了安全问题,但是又出现了 遍历不完全的情况,所以我们需要针对Object可能出现的子类做逐一判断,判断后解决了遍历不完整和安全问题,但是如果集合又添加了一个新的类型的元素,遍历又不完整,需要继续添加else if语句块,所以违背了开闭原则,不好维护,可以违背开闭原则,但是又因为Object类有无数个子类,所以永远无法解决,所以引入了泛型
泛型是模仿数组和方法的特点:
- 模仿数组的 编译时期 确定类型
- 模仿方法的 参数化类型
泛型的概述
泛型是一种独立的技术,和集合无关,是能够将类型在编译时期提前确定的参数化类型
- 泛型的格式: 这里的E必须是引用数据类型,这里可以A~Z
Collection <String> c=new ArrayList<String>();
//集合泛型有传染性在Collection<E>:源码ArrayList会继承<E>,以下省略<E>也正确
Collection <String> c2=new ArrayList();
泛型的好处:
- 1.消除了黄色警告线,提高了安全性
- 2.去除了向下转型,提高了效率
- 3.满足了开闭原则 (提高了扩展性和可维护性)
- 4.提高了可读性
- 5.简化了代
案例:
public static void main(String[] args) {
Collection <String> c=new ArrayList<String>();
//集合泛型有传染性在Collection<E>:源码ArrayList会继承<E>,以下省略<E>也正确
Collection <String> c2=new ArrayList();
c.add("sa");
//c.add(2); 报错
Iterator<String> i=c.iterator();
//迭代器接收c的信息,返回要加<E>的警告,加<E>后消除,且用next接收时,不要向下转型
while(i.hasNext()){
String s=i.next();//不用向下转型
System.out.println(s);
}
}
泛型类:
class Gener<E,H>{
private E e;
private H h;
public E getE() {
return e;
}
public void setE(E e) {
this.e = e;
}
public H getH() {
return h;
}
public void setH(H h) {
this.h = h;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Gener<String,Double> g=new Gener();
g.setE("gg");
String s=g.getE();
g.setH(12.5);
Double d=g.getH();
}
集合的传染性
public static void main(String[] args) {
Collection <String> c=new ArrayList<String>();
//集合泛型有传染性在Collection<E>:源码ArrayList会继承<E>,以下省略<E>也正确
Collection <String> c2=new ArrayList();
c.add("sa");
//c.add(2); 报错
Iterator<String> i=c.iterator();
//迭代器接收c的信息,返回要加<E>的警告,加<E>后消除,且用next接收时,不用向下转型
while(i.hasNext()){
String s=i.next();
System.out.println(s);
}
}
泛型接口
实现接口的三种方式:(个人推荐2、3)
interface Igener<E,H>{
void method(E e);
H test(H h);
}
//-----------------------------------1,对应实现1
class generImpl implements Igener<String,Integer>{
@Override
public void method(String e) {
// TODO Auto-generated method stub
}
@Override
public Integer test(Integer h) {
// TODO Auto-generated method stub
return null;
}
}
//-----------------------------------2,对应实现2
class generImpl2<E,H> implements Igener<E,H>{
@Override
public void method(E e) {
// TODO Auto-generated method stub
}
@Override
public H test(H h) {
// TODO Auto-generated method stub
return null;
}
}
//实现1,注意接口前面的类型一定要定义好,主方法里不能用<E,H>
Igener<String,Integer> i1=new generImpl();
//在实现类就确定好了类型,new后面不能有<String,Integer>,否则报错
//Igener<String,Integer>类型应该和实现类的类型相匹配
i1.test(12);
i1.method("123");
//实现2,<E,H>是在main方法中临时写好的,但是方法中会即时编译
Igener<String,String> i2=new generImpl2<String,String>();
Igener<String,String> i3=new generImpl2();
//Igener<String,String>一定要写,new后面的<String,Integer>可写可不写
i2.method("asd");
i2.test("as");
//实现3,利用其子类的匿名对象实例化
Igener<String,Double> i4=new Igener<String,Double>(){
@Override
public void method(String e) {
// TODO Auto-generated method stub
}
@Override
public Double test(Double h) {
// TODO Auto-generated method stub
return null;
}
};//注意别少了分号
i4.test(12.5);
i4.method("sa");
泛型方法
泛型方法定义:
把泛型定义在方法上,泛型方法独立于泛型类,又称为局部泛型
泛型方法确定类型的时机是方法调用的时候
泛型方法格式: public T[] toArray(T[] a)
class Ger<E>{// 此处E是全局泛型
private E e;
public E getE() {
return e;
}
public void setE(E e) {//不是泛型方法 因为E是全局泛型,方法需要局部泛型
this.e = e;
}
public <T> void show (T t){ //泛型方法1
System.out.println(t.getClass().getName());
}
public <K,V> void test(K k,V v){ //泛型方法2
System.out.println(k);
System.out.println(k.getClass().getName());
System.out.println(v);
}
}
泛型方法测试代码:
注意:有个很重要的方法:toArray()
写法标记:toArray中是我想要实现的数据类 格式为:new 类名[]{}
public static void main(String[] args) {
// TODO Auto-generated method stub
Ger<String> g=new Ger<String>();
g.setE("as");
System.out.println(g.getE());
g.show(8);// 调用泛型方法一,返回对象类名java.lang.Integer
g.test(false, 8.5);// 调用泛型方法2,返回对象名,对象信息
//----------------------------------------false
//java.lang.Boolean
//8.5
Collection<Integer> c=new ArrayList<Integer>();
c.add(1);
c.add(2);
c.add(3);
Integer[] ii1=c.toArray(new Integer[]{} );//记住这种写法,很关键
//捕获c的元素后给它自己定义的类型来接收,不要向下转型
//这个方法很强大,不要向下转型!!!!
for (Integer in : ii1) {//打印深拷贝过来的集合元素
System.out.println(in);
}
}
泛型限定符
泛型限定符
? : 表示泛型类型可以是任意类型
? extends E: 表示泛型类型可以是 E 或者是 E的子类
? super E: 表示泛型类型可以是E 或者是 E的父类
为什么要用到?
因为泛型的声明和实现规定是要一一对应的,比如:
Collection c=new ArrayList();
两个String要一定相同,所以不能出现以下情况
Collection c=new ArrayList();
Collection c=new ArrayList();
因此,我们用?来实现继承和超类
? extends E: 表示泛型类型可以是 E 或者是 E的子类
? super E: 表示泛型类型可以是 E 或者是 E的父类
定义普通类实现继承
class Father{
}
class Son extends Father{
}
class Daughter extends Father{
}
?的使用方法:
? extends T 可以读取到T对象,而不能写入T对象和T的子对象
? super T 可以读取到Object对象,可以写入T对象和T的子对象
下界<? super T>不影响往里存,但往外取只能放在Object对象里
链接:http://www.imooc.com/article/252935?block_id=tuijian_wz
若想既可以读取又可以写入,则不要用通配符,直接用T
public static void main(String[] args) {
// TODO Auto-generated method stub
Collection<Father> c=new ArrayList<Father>();
Collection<?> c2=new ArrayList<Object>();
Collection<? extends Father> c3=new ArrayList<Daughter>();
Collection<?> c4=new ArrayList<Son>();
Collection<?extends Father> c5=new ArrayList<Father>();
Collection<? super Son> c6=new ArrayList<Father>();
Collection<? super Daughter> c7=new ArrayList<Father>();
List<? super Father> c8=new ArrayList<Object>();
//c5.addAll(new ArrayList<Father>());//编译时异常,因为? extends T 只能读取,不能加
c8.addAll(new ArrayList<Father>());//添加成功
System.out.println(c8.get(0));//运行时异常 ?
}