第八章泛型程序设计

泛型程序设计

为什么要使用泛型程序设计:
泛型程序设计(generic programming)意味着编写的代码可以对多种不同类型的对象重用。
例如ArrayList类就可以收集任何类的对象。

定义简单泛型类预定泛型方法

泛型类(generic class)就是有一个或多个类型变量的类。

public class Pair<T>
{
	private T first;
	private T second;
	
	public Pair() { first = null; sencond = null; }
	public Pair(T first, T second) {this.first = first; this.second = second; }
	
	public T getFirst() { return first; }
	....
	public void setSecond(T newValue ) { second = newValue; }
}

泛型方法可以在普通类中定义,也可以在泛型类中定义

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

类型变量的限定

java的继承中,可以根据需要拥有多个接口超类型,但最多有一个限定可以是类。如果有一个类作为限定,它必须是限定列表中的第一个限定。限定类型用“&”隔开,逗号分割类型变量

下面把minmax重写为一个泛型方法。这个方法可以计算泛型数组的最大值和最小值,并返回一个Pair。

package com.day04.pair2;

import com.day04.Pair;

import java.time.*;

/**
 * @Author xcf
 * @date 2020-12-28
 */
public class PairTest2 {
    public static void main(String[] args) {
        LocalDate[] birthdays = {
                LocalDate.of(1906,12,9), // G. Hopper
                LocalDate.of(1815,12,10), // A. Lovelace
                LocalDate.of(1903,12,3), // J.von Neumann
                LocalDate.of(1910,6,22) // K. Zuse
        };
        Pair<LocalDate> mm = ArrayAlg.minmax(birthdays);
        System.out.println("min = " + mm.getFirst());
        System.out.println("max = " + mm.getSecond());
    }
}

class ArrayAlg{
    /**
     * 求T型对象数组的最小值和最大值
     * @param a T型对象的数组
     * @return 具有最小值和最大值的对,如果a为空,则为null
     */
    public static <T extends Comparable> Pair<T> minmax(T[] a){
        if (a == null || a.length == 0) return null;
        T min = a[0];
        T max = a[0];
        for (int i = 0; i < a.length; i++) {
            if (min.compareTo(a[i]) > 0 ) min = a[i];
            if (max.compareTo(a[i]) < 0 ) max = a[i];
        }
        return new Pair<>(min,max);
    }
}

泛型代码与虚拟机

虚拟机没有泛型类对象——所有对象都属于普通类。

类型擦除

无论何时定义一个泛型类型,都会自动提供一个相应的原始类型(raw type)。这个原始类型的名字就是去掉类型参数或的泛型类型名。类型变量会被擦除(erased),并替换为其限定类型(或者,对于无限定的变量则替换为Object)

转换泛型方法

类型擦除也会出现类型方法中。
对于泛型的转换,需要记住以下几个事实:

  • 虚拟机中没有泛型,只有普通的类和方法。
  • 所有的类型参数都会替换为它们的限定类型。
  • 会合成桥方法来保持多态。
  • 为保持类型安全性,必要时会插入强制类型转换。

限定与局限性

  1. 不能用基本类型实例化类型参数
  2. 运行时类型查询只适用于原始类型
  3. 不能创建参数化类型的数组
  4. Varargs警告( @SuperssWarinings(“unchecked”) )
  5. 不能实例化类型变量
  6. 不能构造泛型数组
  7. 泛型类的静态上下文中类型变量无效
  8. 不能抛出或铺货泛型类的实例
  9. 可以取消对检查型异常的检查
  10. 注意擦除后的冲突

泛型类型的继承规则

首先思考一个问题,现在有一个 Employee 类和一个继承了 Employee 类的 Manager 类,如下所示:

public class Employee { ... }
public class Manager extends Employee { ... }

那么对于一个普通的泛型类 Practical:

public class Practical<T> { ... }

类型 Practical< Manager > 是 Practical< Employee > 的子类吗?答案是否定的,通过下面这段代码可以对此进行验证:

	Practical<Manager> manager = new Practical<>();
	Practical<Employee> employee = manager; // error

上述代码的第二行,用一个 Practical< Employee > 类型的变量引用了一个 Practical< Manager > 类型的对象,然而编译器显示这样是不合法的。我们知道,如果 B 是 A 的子类的话,是可以用 A 类型的变量引用 B 类型的对象(向上造型)的。显然,这个例子证明了 Practical< Manager > 并不是 Practical< Employee > 的子类。

也许这看起来会很奇怪,但这样设定是出于安全性的考虑,可以避免一些风险的出现。

public class Practical<T> {
	ArrayList<T> list;
	
	public Practical() {
		list = new ArrayList<>();
	}
	
	public void add(T t) {
		list.add(t);
	}
	
	public T getFirst() {
		// return the top ranked;
	}

现在我们补全了一部分 Practical 类的内容,然后执行以下操作:

	Practical<Manager> manager = new Practical<>();
	Practical<Employee> employee = manager; // 假设可以
	manager.add(new Manager());
	employee.add(new Employee());
	Employee firstEmployee = getFirst();

观察上面的代码,就会发现很多不合理之处,首先第一行实例化了一个新的对象之后,此对象中的 list 列表的成员按道理应该是 Manager 类型的,在第三行向 list 中加入了一个 Manager 类型的对象之后,第四行又加入了一个 Employee 类型的对象(???此处 Employee 可是 Manager 的父类)。在此之后还选出了 list 中的第一名(暂且理解为绩效最好的吧),将“经理”和“普通员工”放一起比较显然是不合理的。

当然,Java泛型类型的继承规则保证了这种“离谱”的情况将不会发生!

泛型及通配符方法综合

package com.day04.pair3;

import com.day02.equals.*;
import com.day04.Pair;

/**
 * @Author xcf
 * @date 2020-12-28
 */
public class PairTest3 {

    public static void main(String[] args) {
        Manager ceo = new Manager("Gus Greedy",80000,2003,12,15);
        Manager cfo = new Manager("Sid Sneaky",60000,2003,12,15);
        Pair buddies = new Pair<Manager>(ceo, cfo);
        printBuddies(buddies);

        ceo.setBonus(1000000);
        cfo.setBonus(500000);
        Manager[] managers = { ceo, cfo};

        Pair result = new Pair<Employee>();
        minmaxBonus(managers,result);

        System.out.println("first:" + result.getFirst().getClass().getName()
                        + ", second:" + result.getSecond().getClass().getName());

    }

    public static void printBuddies(Pair<? extends Employee> p){
        Employee first = p.getFirst();
        Employee second = p.getSecond();
        System.out.println(first.getName() +" and " + second.getName() + "are buddies." );
    }

    public static void minmaxBonus(Manager[] a, Pair<? super Manager> result){
        if (a.length == 0) return;
        Manager min = a[0];
        Manager max = a[0];
        for (int i = 0; i < a.length; i++) {
            if (min.getBonus() > a[i].getBonus()) min = a[i];
            if (max.getBonus() < a[i].getBonus()) max = a[i];
        }
        result.setFirst(min);
        result.setSecond(max);
    }

    public static void maxminBonus(Manager[] a, Pair<? super Manager> result){
        minmaxBonus(a,result);
        PairAlg.swapHelper(result);// swapHelper捕获通配符类型
    }
    //不能写 public static <T super manager>. . .
}

class PairAlg{
    public static boolean hasNulls(Pair<?> p){
        return p.getFirst() == null || p.getSecond() == null;

    }

    public static void swap(Pair<?> p){
        swapHelper(p);
    }

    public static <T> void swapHelper(Pair<T> p){
        T t = p.getFirst();
        p.setFirst(p.getSecond());
        p.setSecond(t);
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值