详解Java泛型(一)之简单介绍

1. 概述

泛型是Java SE 1.5的新特性,泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。什么叫参数化类型呢?像java的方法有形参,然后调用方法的时候传递实参。而参数化类型,顾名思义就是把类型参数化,就是说类型是不固定的,是靠调用者传入进来的。

这种参数类型可以用在类、接口和方法的创建中,分别称为泛型类、泛型接口、泛型方法。 Java语言引入泛型的好处是安全简单。

在Java SE 1.5之前,没有泛型的情况的下,通过对类型Object的引用来实现参数的“任意化”,“任意化”带来的缺点是要做显式的强制类型转换,而这种转换是要求开发者对实际参数类型可以预知的情况下进行的。对于强制类型转换错误的情况,编译器可能不提示错误,在运行的时候才出现异常,这是一个安全隐患。

比如说ArrayList类,在没有泛型的时候,获取的元素是Object类型的,要使用该元素,必须知道该元素的具体类型,并对其进行显示强制类型转换,而且一不小心就可能会抛出ClassCastException异常,不仅麻烦还不安全。就像下面的代码那样。

import java.util.ArrayList;
import java.util.List;

public class ArrayListDemo {

    public static void main(String[] args) {
        List list = new ArrayList();
        //在list中添加一个字符串元素
        list.add("123");
        //正常类型转换
        String str = (String) list.get(0);
        //会出现转换异常
        Integer val = (Integer) list.get(0);
    }
}

泛型的好处是在编译的时候检查类型安全,并且所有的强制转换都是自动和隐式的,以提高代码的重用率。 如上面的例子用上了泛型,就可以在编译的时候知道错误。

import java.util.ArrayList;
import java.util.List;

public class ArrayListDemo {

    public static void main(String[] args) {
        List<String> list = new ArrayList<String>();
        //在list中添加一个字符串元素
        list.add("123");
        String str = list.get(0);// 不用进行类型转换
        Integer val = (Integer) list.get(0);// 编译出错
    }
}

下面就来具体介绍一下泛型的用法

2.类型参数

类型参数就是尖括号(“<>”)里的东西,就像是getVal(Intger val)的方法参数是Intger类型变量,到时候调用这个方法的时候传入Intger类型的实参即可,而类型参数传入的不是一个数据类型的变量,而是一个数据类型,如上面代码List<String> list = new ArrayList<String>();的就是传入了一个String类型。

3. 定义泛型类

泛型类即带类型参数的类,像ArrayList类就是带有类型参数的类也就是泛型类,那我们怎么自己定义一个泛型类呢?很简单,在类名的后面加上<>并在里面指定一个或多个类型变量,多个类型变量时使用逗号隔开。 比如说我有一个类,可以存储两个相同类型的值,代码就可以这样子实现。

public class Pair<T> {
    private T t1;
    private T t2;
    public T getT1() {
        return t1;
    }
    public void setT1(T t1) {
        this.t1 = t1;
    }
    public T getT2() {
        return t2;
    }
    public void setT2(T t2) {
        this.t2 = t2;
    }
}

可以看到Pair类有一个类型变量T,这个类型我们并不知道是什么,只有实例化的时候传进来才会知道,然后可以像下面的代码那样实例化泛型类

public class Main {

    public static void main(String[] args) {
        Pair<String> p = new Pair<String>();
        p.setT1("这是t1");
        p.setT2("这是t2");
        String t1 = p.getT1();
        String t2 = p.getT2();
        System.out.println(t1);
        System.out.println(t2);
    }
}

可以看到我用一个String类型作为实际的类型变量传进去,这样子Pair的set和get方法都是针对String类型的了。

4. 泛型方法

实际上,还可以定义一个带有类型变量的简单方法。如下面的代码

public class ArrayMiddle {

    public static <T> T getMiddle(T ... ts){
        return ts[ts.length/2];
    }
}

这里的泛型方法并不是定义在泛型类中的,而是定义在一个普通类中。其实泛型方法可以定义在普通类中,也可以定义在泛型类中。需要注意的是,类型参数放在修饰符(这里是public static)的后面,返回类型之前。这个泛型方法的功能是接受一些相同类型的变量(数量不确定),然后返回这些变量当中中间的那个变量。

可以像下面那样使用泛型方法

public class Main {

    public static void main(String[] args) {
        String middle = ArrayMiddle.<String>getMiddle("a","b","c");
        System.out.println(middle);
    }
}

输出结果是b

当然也可以像String middle = ArrayMiddle.getMiddle("a","b","c");这样子调用泛型方法(一般我们都是这么用的),编译器可以根据上下文信息推断出该类型变量就是String类型。

5.类型变量的限定

现在考虑一个问题,我想让上面的泛型类Pair有一个getMax方法可以返回t1和t2中的最大值,那么我这的代码写出下面这样

public class Pair<T> {
    private T t1;
    private T t2;
    public T getT1() {
        return t1;
    }
    public void setT1(T t1) {
        this.t1 = t1;
    }
    public T getT2() {
        return t2;
    }
    public void setT2(T t2) {
        this.t2 = t2;
    }

    public T getMax(){
        int result = t1.compareTo(t2);
        if(result > 0)
            return t1;
        else
            return t2;
    }
}

当然这段代码是不能编译通过的,因为你根本就不知道泛型变量T会是什么,很多类是没有compareTo方法的,但是实现了Comparable接口的类就会有这个方法。所以解决这个问题的的方案就是将T所属的类限制成实现了Comparable接口的类。所以我修改一下将Pair<T>变为 Pair<T extends Comparable<T>>,就可以编译通过了。然后使用一下这个方法

public class Main {

    public static void main(String[] args) {
        Pair<Integer> p = new Pair<Integer>();
        p.setT1(11);
        p.setT2(5);
        Integer max = p.getMax();
        System.out.println(max);

    }
}

输出结果为11

需要注意的是,因为Integer类实现了Comparable接口,所以可以像上面那样使用。而像Thread类是没实现Comparable接口的,如果将Thread类传入类型变量,编译就会出错,所以实例化的时候不可以传入没有实现Comparable接口的类型变量,这样子就把类型变量限定住了。

还需要注意的是,虽然Comparable是接口,但是还是用extends关键字来限定,而不是用implements关键字。无论是类或者接口都是用extends关键字来限定的。

多个限定类型之间用”&”分隔,如Pair<T extends Comparable & Serializable>

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值