泛型与通配符

泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。

那么,我们为什么要使用泛型呢?
首先,我们写一个通用类型的栈

class ObjectStack{
    private Object[] elem;
    private int top;//

    public ObjectStack(){
        this(10);
    }

    public ObjectStack(int size){
        this.elem = new Object[size];
        this.top = 0;
    }

    public void push(Object val){
        this.elem[this.top++] = val;
    }

    public void pop(){
        --this.top;
    }


    public Object getTop(){
        return this.elem[this.top-1];
    }



}

public class ObjectStackDemo {

    public static void main(String[] args) {
        ObjectStack s1 = new ObjectStack(10);
        s1.push(10);
        s1.push(20);
        s1.push("hello");


        String data = (String)s1.getTop();//必须进行强制转换,如果不进行强制转换,会报错
        System.out.println(data);

    }

}

由于 Object 是所有类的父类,所有的类都可以作为成员被添加到上述类中;当需要使用的时候,必须进行强制转换,而且这个强转很有可能出现转换异常。

使用 Object 来实现通用、不同类型的处理,有这么两个缺点:
1,每次使用时都需要强制转换成想要的类型
2,在编译时编译器并不知道类型转换是否正常,运行时才知道,不安全
所以,我们在此引入了泛型

利用泛型写一个栈

class Stack<T>{//这里的T一个类型占位符
    public T  []elem;
    int top;
    public Stack(){
        this(10);
    }
    public Stack(int i) {
        //this.elem=new T[i];我们不能new一个泛型类型的数组
        this.elem=(T[])new Object[i];
        this。.top=0;
            }
    public void rush(T val){
        this.elem[this.top]=val;
        top++;
    }
    public void pop(){
        this.elem[this.top-1]=null;
        this.top--;
    }


    public T gettop(){
        return this.elem[this.top-1];
    }

}

这里写图片描述
上述图片中我们可以看到,在代码中,当我们规定T为Integer类型的时候,如果我们入栈一个非Integer类型的数据时候,编译器在编译过程中就会报错。
由此可见,泛型的意义在于它可以在编译期间就可以对数据类型进行检查。并且在得到数据时,我们不必进行强制转换

实际上,我们可以引入泛型的意义在于俩点:
1,对类型进行自动检查 编译过程中
2,会进行自动类型转换

泛型的擦除机制:
在编译期间,所有的泛型信息都会被擦除,如上面代码中的Ingeter类型,在编译后都会变成object类型。这也是Java的泛型被称为“伪泛型”的原因。

使用泛型的过程中,我们要注意以下几点:
1.不能new泛型类型的数组
2.不能new泛型类型的对象
3.不能得到泛型对象的数组
4.简单类型不能作为泛型的参数,因为泛型有类型擦除机制,但是简单类型没有基类
5.在static方法当中,不能用泛型类型的参数
因为static方法不依赖于对象 , 所以无法指定当前泛型

泛型类与泛型的上界
这里写图片描述

GenericAlg是一个泛型类
如上图所示,我们要找到一个泛型数组中最大的元素,但是,我们发现,编译期发生了报错,这是为什么?

上面过程中我们提到了泛型的类型擦除,所有的类型都会被擦出到他的基类,object,但是。在object类中,我们并没有实现Comparable接口中的compareTo方法,所以,此时会报错。

此时,就引出了我们所说的泛型的上界。

class GenericAlg<T extends Comparable<T>>{//T extends .... 表示泛型的上界,在这里他表示的是泛型的实现了Comparable接口的基类

    public T findMaxVal(T[] array){
        T maxVal = array[0];
        for(int i = 1;i < array.length;i++) {
            if(maxVal.compareTo(array[i]) < 0){
                maxVal = array[i];
            }
        }
        return maxVal;
    }
}

泛型方法
我们可以将上面的方法改写成static的方法,这样,我们就可以直接通过类名.方法名来使用上面的方法

class GenericAlg2{
    /**
     * 泛型方法
     */
    public static<T extends Comparable<T>> T findMaxVal(T[] array){//在static后面
        T maxVal = array[0];
        for(int i = 1;i < array.length;i++) {
            if(maxVal.compareTo(array[i]) < 0){
                maxVal = array[i];
            }
        }
        return maxVal;
    }

}

通配符及通配符的上下界

在实例化对象的时候,不确定泛型参数的具体类型时,可以使用通配符进行对象定义
通配符的下界:
? super T
通配符的上界:
? extends T

通配符下界的用法

class Person implements Comparable<Person>{
    private String name;

    public Person(String name) {
        super();
        this.name = name;
    }

    @Override
    public String toString() {
        return "Person [name=" + name + "]";
    }

    @Override
    public int compareTo(Person o) {
        // TODO Auto-generated method stub
        return name.compareTo(o.name);
    }

}

class Student extends Person{
    private int age;

    public Student(String name, int age) {
        super(name);
        this.age = age;
    }

    @Override
    public String toString() {
        return "Student [age=" + age + "]";
    }


}

上面代码中我们创建了俩个类,一个person类,另一个类student继承了person类

class GenericAlg3{

    //泛型实现
    public static<T extends Comparable<T>> T findMaxPerson1(ArrayList<T> list){

        T maxPerson = list.get(0);
        for(int i = 1;i < list.size();i++){
            if(maxPerson.compareTo(list.get(i)) < 0){
                maxPerson = list.get(i);
            }
        }

        return maxPerson;
    }



在这个类中,我们写了得到容器内最大值的方法
这里写图片描述
上面代码中我们可以看到,person类实现了comparable接口,所以,对于list2对象使用上述方法,他并没有报错,但是,student类由于没有实现comparable接口,所以编译期进行了报错,但是,由于student类继承了person类,所以,我们也没有必要在student类中再实现一次comparable接口,此时,我们就用到了通配符的下界

public static<T extends Comparable<? super T>> T findMaxPerson(ArrayList<T> list){
        //? super T  找到是否有T类型的基类实现了Comparable接口
        T maxPerson = list.get(0);
        for(int i = 1;i < list.size();i++){
            if(maxPerson.compareTo(list.get(i)) < 0){
                maxPerson = list.get(i);
            }
        }

        return maxPerson;
    }

}

通过加入通配符的下界,student的对象也可以调用上述方法。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值