java学习之路的自我回顾6(泛型1)

来源:互联网 发布:大数据时代的企业管理 编辑:程序博客网 时间:2024/06/08 14:22

泛型1

当使用集合时,可能会出现类型不统一的问题,从而导致程序报错。大多数的情况如下

public static void main(String args[]){    List lList=new ArrayList();    lList.add("hello java");    lList.add("hello python");    lList.add("hello world");    lList.add(5);//a    for(int i=0;i<lList.size();i++)    {        System.out.println((String)lList.get(i));//b    }}

这时在a处,不小心放入集合的是一个Integer类型,而在b处程序想要将一个Integer类型的数强制变为String类型的进行输出,因此程序会抛出ClassCastException异常
(程序试图把一个Integer转化为String类型)。

这时如果我们想要创建一个List集合,让ta只能存储String类型的对象的话,我们可以手动的实现类型控制
如下

class myList{private List lList=new ArrayList();public boolean add(String s){       return lList.add(s);}public String get(int x){    return (String)lList.get(x);}public int size(){    return lList.size();}}public static void main(String args[]){    myList lList=new myList();    lList.add("hello java");    lList.add("hello python");    lList.add("hello world");    lList.add(5);//a    for(int i=0;i<lList.size();i++)    {        System.out.println((String)lList.get(i));//b    }}

这时候,如果不小心输入了一个Integer类型的数进入集合,就像a处一样,那么程序就会报错,这样就达到了控制集合类型的目的了,但是手动的方式必然会消耗
编程人员的大量时间和精力,并且由于每次都要定义一个List类,使得程序变得复杂,不简洁。

在这之后(JDK1.5以后),java加入了“参数化类型”的概念,允许我们在创建集合时规定集合的类型,java的“参数化类型”被称为泛型
进行这一修改后,集合的操作变得更加简洁,方便了
如下

public static void main(String args[]){    List<String> lList=new ArrayList(String);    lList.add("hello java");    lList.add("hello python");    lList.add("hello world");    lList.add(5);//a    for(int i=0;i<lList.size();i++)    {        System.out.println((String)lList.get(i));//b    }}

在a处,将会直接引起程序的编译异常。因为集合中只能加入String对象,而现在想要加入集合的是一个Integer对象

泛型
泛型就是允许在定义类,接口时指定类型形参,这个类型形参将会在声明变量,创建对象时确定(即传入实际的类型参数)

JDK1.5以后,改写后的List接口,Iterator接口的部分片段如下

public interface List<E>{    void add(E x);    Iterator<E> iterator(); } public interface Interator<E>{    E next();    boolean hasNext();}

可以明显的看出,在创建List集合或者其他的变量,类时,可以通过传入不同的形参来创建不同的对象,类
简单来说,包含泛型声明的类型可以在定义变量,创建对象时传入一个类型实参,从而动态生成无数多个逻辑上的子类
但这种子类在物理上并不存在

那么我们是否也能类似使用泛型,给一个类增加泛型声明呢,答案是肯定的
如下

public class A <T>{private T x;public A(){};public A(T z){this.x=z;}public T getX(){return x;}public static void main(String args[]){    A<String> a=new A<String>("hello world");    System.out.println(a.getX());}}

输出 hello world
虽然看上去并没有声明实际意义,泛型主要的应用场合还是在集合,但是能够通过上面的例子来加深对泛型的理解,通过泛型声明,就可以通过传入不同类型的参数来产生无数个
子类。

继承

这里有一点需要注意,当父类已经泛型声明了,那么继承ta的子类不能够再泛型声明,

即 public class B extends A<T>{} 是错误的子类的声明方式有多种1.public class B extends A<String>2.public class B extends A

这里有一点比较重要,当使用方法时,需要为所有的数据形参传入数据值;而使用类,接口的时候,可以不用为类型形参传入实际类型,所以上面第2中方式是可行的的

当一个子类继承了带泛型声明的父类并且想要重写方法时,需要注意一点
以声明方式1举例(声明方式2比较特殊,之后再讲2)

public class B extends A<String>{    //a.......正确    public String getX()    {        return "子类"+super.getX();    }    //b......错误    public Object getX()    {        return "子类";    }}

因为在子类创建时就已经将String类型传给了父类,所以父类中的T都应该变为了String

声明方式2
因为没有传入类型,所以父类中的T类型默认是Object类型的,如下

public class B extends A{    public String getX()    {        return super.getX().toString();    }}

将值toString一下后才能得到String类型的数据

类型通配符
假设现在我们需要一个方法,该方法有一个带泛型声明的形参,现在想要调用该方法,该如何做?
我们传统的想法一定是这样的,虽然传入的实参的类型不定,但是只要形参是实参的父类就可以了吧,如下

public way(A<Object> x){    .................}
然而这样写程序将会报错,因为在程序看来,A<Object> 并不是A<String>,A<int>等等的父类,所以会报错即X为Y的父类,但是A<X>不是A<Y>的父类,这和我们的传统思维略有不同所以,诞生了类型通配符这一概念,即将类型写为 ? 即可,如下
public way(A<?> x){    .................}

这时候调用方法,将不会报错

A<String> a=new A<String>;way(a);

但是值得注意的是,这种带通配符的List只是各种泛型List的父类,ta实际上并不存在,也不能给其添加元素;

A<?> a=new A<?>;a.add("hello");//错误
0 0
原创粉丝点击