Java中的泛型

1、泛型类

注意< T >的位置,T可以是任意的字母,常用的有K、V、T、S、U等,多个类型变量变量可以用逗号分割Pair < T, K, V >。

 class Pair<T> {
    private T min;
    public Pair() {
        this.min = null;
    }
    public Pair(T min) {
        this.min = min;
    }
    public T getMin() {
        return min;
    }
    public void setMin(T min) {
        this.min = min;
    }
}

2、泛型方法

下边是一个简单的泛型方法,注意类型变量(< T >)放在修饰符的后边(public、static),在返回值类型前边(T)。

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

对此方法的调用也有两种形式,一种指定类型变量,另一种编译器可以根据参数推断

ArrayAlg.<Integer>getMiddle(1, 2);
ArrayAlg.getMiddle(1, 2);

当没有指定类型变量,并且根据参数推断时发现不是同一种类型,会继续向上推断,直至找到共同的父类,这个父类就是推断出来的类型变量了,如下,第一行推断结果为Number,第二行为Object

Number n = ArrayAlg.getMiddle(1, 2, 2.2);
Object o = ArrayAlg.getMiddle(1, 2.2, "hello");

3、泛型变量限定

为什么要限定泛型变量呢?如果在方法中需要调用T类型的对象的一个方法,但是这个方法有的类有有的类没有,那么就限制这个类型变量必须是指定有这个方法的子类,包括继承和实现接口,如下将T类型变量限制为实现Comparable接口的类,这样就可以调T类型变量的compareTo()方法而不会报错了。

public static <T extends Comparable<T>> T min(T[] ts) {
    if (ts == null || ts.length == 0)
        return null;
    T min = null;
    for (T t : ts)
        min = min.compareTo(t) > 0 ? t : min;
    return min;
}

语法为< T extends BoundingType >,接口和继承都用extends,多限制可以用&分割< T extends BoundingType1 & BoundingType2 >

4、泛型代码和虚拟机

  • 编译的时候类型参数会被擦除掉,替换为限定的类型,没有限定类型的话替换为Object,注意:限定类型是指< T extends BoundingType >中的BoundingType,而不是实例化时像List< String >中的String

  • 如果有多个限定,会选择第一个限定作为擦除后的替换,例如在上边的例子中将类型限定改为< T extends Comparable & Serializable >,将会采用Comparable来替换T,但是如果写成< T extends Serializable & Comparable >,将会使用Serializable来替换T,但是之后的代码中会执行compareTo()方法,编译器就要做必要的类型转化了,所以为了提高效率,应将标签接口(没有方法的接口)放到边界列表的末尾(边界列表就是指上边的多个类型限定)

  • 在擦除的的时候会有一些必要的强制类型转化,其中脉络比较复杂,也牵扯了泛型的核心,这里不展开说明了。

5、通配符

用如下代码讲解通配符的作用和简单用法
public class Main {
    public static void main(String[] args) {
        Utils.printEmployee(new Pair(new Manager("Job"), new Manager("Ketty")));
    }
}

class Utils {
    // 方法一:不能接收Manager
    public static void printEmployee(Pair<Employee> e) {
        System.out.println("first:" + e.getFirst().getName());
        System.out.println("second:" + e.getSeconde().getName());
    }

    // 方法二:能接收Manager
    public static void printEmployee(Pair<? extends Employee> e) {
        System.out.println("first:" + e.getFirst().getName());
        System.out.println("second:" + e.getSeconde().getName());
    }
}

class Pair<T> {
    private T first;
    private T seconde;

    public Pair(T first, T second) {
        this.first = first;
        this.seconde = second;
    }
    // getter and setter...
}

class Employee {
    private String name;
    public Employee(String name) {
        this.name = name;
    }
    // getter and setter...
}
class Manager extends Employee {
    private int rank;
    public Manager(String name) {
        super(name);
    }
    // getter and setter...
}
  • Manager继承自Employee,工具类中有一个方法printEmployee()希望可以去打印Pair类中存放的两个Employee的name信息,自然也可以想到,Manager继承自Employee,那么应该也可以打印Manager的name信息。
  • 那么明确一点Pair< Manager >不是Pair< Employee >的子类,而且他们没有任何关系
  • 那么问题来了,根据上一点说的,Pair< Manager >不是Pair< Employee >的子类,那方法一
    printEmployee(Pair< Employee > e) 也就没法接收Pair< Manager >类型的参数,那么main方法中的调用也就自然而然报错,这就不符合我们自然的想法了,怎么解决呢?用通配符!
  • 方法二 printEmployee(Pair< ? extends Employee > e) 需要的参数是Pair< ? extends
    Employee >类型,而Pair< Manager >是其子类,所以可以成功。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值