Java中的泛型概念学习

在阅读Java源码时配到了泛型的使用。本着知其然知其所以然的原则,上网搜索了资料学习。总结如下:

什么是泛型

泛型即类型参数化。
通俗易懂的讲就是,类,方法所操作的类型被指定为一个参数,在用到的时候再指定具体的类型。

public class Test {
    public static void main(String[] args){
        Box<String> sbox = new Box<String>();//应用时指定数据类型为String
        Box<Integer> ibox = new Box<Integer>();
    }
}
//申明时操作的数据类型被指定为T
class Box<T> {
     private T data;
     public Box() {

     }
     public Box(T data) {
         this.data = data;
     }
     public T getData() {
         return data;
     }
} 

泛型的意义

个人认为泛型的意义有2个:
-数据类型的检查
-代码的复用

  1. 类型检查
    下面的代码中List未限定数据类型,默认处理的为object。所以下面的列子中可以同时往里添加String和Integer而不会在编译过程中报错。但是在运行过程Integer无法强制转换成String,会报出如下错误。
public class Test {
    public static void main(String[] args){
        List list = new ArrayList();
        list.add("111");
        list.add(222);

        for (int i = 0; i < list.size() ; i ++){
            String value = (String)list.get(i);
            //java.lang.ClassCastException: java.lang.Integer cannot be cast to java.lang.String
            System.out.println(value);
        }
    }
}

如果指定类型,下面注释的代码就会报错。将运行时的错误提前到编译时暴露出来总是有益的。

public class Test {
    public static void main(String[] args){
        List<String> list = new ArrayList<String>();
        list.add("111");
//      list.add(222);

        for (int i = 0; i < list.size() ; i ++){
            String value = (String)list.get(i);
            System.out.println(value);
        }
    }
}
  1. 代码的复用
    如第一个例子代码,一个泛型类。可以处理String类型数据,也能处理Integer类型数据而不必再新建一个类。

自定义泛型,泛型类,泛型接口和泛型方法

-如示例一中的Box类即是最简单的泛型类了。
-泛型接口

interface Number<T>{
    public void SelfAdd(T number);
}

-泛型方法

class Type{
    //泛型方法
    public <T> T getType(T x){
        return x;
    }
}

在使用泛型时,我们会指定具体的数据类型。那么相同的泛型类指定不同的数据类型后生成的对象,其类型是否还一致呢?

public class Test {
    public static void main(String[] args){
        Box<String> sbox = new Box<String>();
        Box<Integer> ibox = new Box<Integer>();
        System.out.println(sbox.getClass() == ibox.getClass()); //true
    }
}

通过如上例子,我们可以知道。即使指定了不同的数据类型,生成对象后其类型还是相同的。
所以在逻辑上sbox和ibox可以看成是不同的类型,实际上是用一个基本类型即Box。

类型通配符

既然我们定义了泛型,总是要使用他的。但是泛型的具体数据类型不同,我们总不可能为每一种用到的数据类型申明一个同样的操作。如下代码所示:

    public static void main(String[] args){
        Box<String> sbox = new Box<String>("Name");
        Box<Integer> ibox = new Box<Integer>(123);

        ShowData(sbox);
        ShowData(ibox);//编译错误
    }

    public static void ShowData(Box<String> box){
        System.out.println(box.getData());
    }

我们申明了方法ShowData处理Box<String>。因应Box<String>Box<Integer>并无继承派生关系,我们不能够直接用ShowData处理Box<Integer>。从这似乎不符合多态性。
因此我们需要一中方法能同时表示Box<String>Box<Integer>。类型通配符应运而生。

类型通配符一般是使用 ? 代替具体的类型实参。注意了,此处是类型实参,而不是类型形参!且Box<?>在逻辑上是Box<Integer>Box<Number>…等所有Box<具体类型实参>的父类。由此,我们依然可以定义泛型方法,来完成此类需求。

    public static void main(String[] args){
        Box<String> sbox = new Box<String>("Name");
        Box<Integer> ibox = new Box<Integer>(123);

        ShowData(sbox);//输出Name
        ShowData(ibox);//输出123
    }

    public static void ShowData(Box<?> box){
        System.out.println(box.getData());
    }

泛型中的类型限定

我们有如下列子:

    public static<T> T Compare(T t1,T t2){
        if (t1.compareTo(t2) > 0 ) //编译报错
            return t1;
        else
            return t2;
    }

由于在编码阶段,我们未指定具体类型所以其默认为object类型出来。object类型中并未有compareTo方法,所以会编译报错。
在这种情况下,我们需要对泛型参数做些限定。总所周知,实现了 Comparable接口的方法都有compareTo方法。所以可以做如下限定:

    public static<T extends Comparable> T Compare(T t1,T t2){
        if (t1.compareTo(t2) > 0 ) 
            return t1;
        else
            return t2;
    }

类型限定有如下注意点:
1. 不管该限定是类还是接口,统一都使用关键字 extends
2. 可以使用&符号给出多个限定
3. 如果限定既有接口也有类,那么类必须只有一个,并且放在首位置

–通配符的上下边界:
当我们需要限定通配符为某一族中的一部分时,就需要使用通配符的上下边界限定了。如下例子:

public class Test {
    public static void main(String[] args){
        Box<GrandFather> Gf = new Box<GrandFather>(new GrandFather());
        Box<Father> Fa = new Box<Father>(new Father());
        Box<Child> Ch = new Box<Child>(new Child());

        print1(Gf);//编译错误
        print1(Fa);
        print1(Ch);

        print2(Gf);
        print2(Fa);
        print2(Ch);//编译错误
    }

    public static void print1(Box<? extends Father> b1){
    }

    public static void print2(Box<? super Father> b2){
    }
}

class Box<T extends GrandFather>{
    private T data;
    public Box() {
    }
    public Box(T data) {
        this.data = data;
    }
    public T getData() {
        return data;
    }
}

class GrandFather{
}

class Father extends GrandFather{
}

class Child extends Father{
}

由上诉例子中我们可以知道
extends: 界定上边界,即泛型方法只接受Father以及其子类。
super:界定下边界,即泛型方法只接受Father以及其父类。
限定通配符总是包括自己。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值