泛型程序设计

一、泛型类

  1. 泛型类:
package pair1;

//一个泛型类就是具有一个或多个类型变量的类,Pair<T, U>{...}
//一般使用E表示集合的元素类型,K和V分别表示表的关键字与值的类型,T(或U、S等)表示“任意类型”
public class Pair<T> {
    private T first;
    private T second;

    public Pair() {
    }
    public Pair(T first, T second) {
        this.first = first;
        this.second = second;
    }

    public T getFirst() {
        return first;
    }
    public T getSecond() {
        return second;
    }
    public void setFirst(T first) {
        this.first = first;
    }
    public void setSecond(T second) {
        this.second = second;
    }
}
  1. 使用方法类:
package pair1;

public class ArrayAlg {
    public static Pair<String> minmax(String[] a) {
        if (a == null || a.length == 0)
            return null;
        String min = a[0];
        String max = a[0];

        for (int i = 1; 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);
    }
}
  1. 测试:
package pair1;

public class PairTest {
    public static void main(String[] args) {
        String[] words = {"a", "Mary", "had", "little", "lamb"};
        Pair<String> mm = ArrayAlg.minmax(words);
        System.out.println("min = " + mm.getFirst());
        System.out.println("max = " + mm.getSecond());
    }
}

二、泛型方法

package pair1;

public class ArrayAlg {
    //泛型方法可以定义在普通类中,也可以定义在泛型类中
    //类型变量放在修饰符后面,返回类型的前面
    public static <T> T getMiddle(T...a) {
        return a[a.length/2];
    }
}

三、类型变量的限定

泛型类:

package pair2;

public class Pair2<T> {
    private T first;
    private T second;

    public Pair2() {
    }
    public Pair2(T first, T second) {
        this.first = first;
        this.second = second;
    }

    public T getFirst() {
        return first;
    }
    public T getSecond() {
        return second;
    }
    public void setFirst(T first) {
        this.first = first;
    }
    public void setSecond(T second) {
        this.second = second;
    }
}

限定类:

package pair2;

class ArrayAlg2 {
    //可以对类型变量T设置限定<T extends BoundingType>,T应该是绑定类型的子类型,T和绑定类型可以是类,也可以是接口
    //一个类型变量或通配符可以有多个限定,T extends Comparable & Serializable,限定类型用&分隔,用逗号分隔类型变量
    //Java继承中可以有多个接口超类型,只能有一个类,且类要在限定列表中的第一个
    public static <T extends Comparable> Pair2<T> minmax(T[] a){
        if (a == null || a.length == 0)
            return null;
        T min = a[0];
        T max = a[0];
        for (int i = 1; i < a.length; i++){
            if (min.compareTo(a[i]) > 0)
                min = a[i];
            if (max.compareTo(a[i]) < 0)
                max = a[i];
        }
        return new Pair2<>(min, max);
    }
}

测试:

package pair2;

import java.time.LocalDate;

public class PairTest2 {
    public static void main(String[] args) {
        LocalDate[] birthday = {
                LocalDate.of(1906, 12, 9),
                LocalDate.of(1815, 12, 10),
                LocalDate.of(1903, 12, 3),
                LocalDate.of(1910, 6, 22),
        };
        Pair2<LocalDate> mm = ArrayAlg2.minmax(birthday);
        System.out.println("min = " + mm.getFirst());
        System.out.println("max = " + mm.getSecond());
    }
}

四、泛型代码和虚拟机

虚拟机没有泛型类型变量,所有的对象都属于普通类。

1. 类型擦除

无论何时定义一个泛型类型,都自动提供了一个相应的原始类型。擦除类型变量,原始类型用第一个限定的类型变量来替换,如果没有给定限定就用Object替换。
Pair泛型类型:

public class Pair<T> {
    private T first;
    private T second;

    public Pair() {
    }
    public Pair(T first, T second) {
        this.first = first;
        this.second = second;
    }

    public T getFirst() {
        return first;
    }
    public T getSecond() {
        return second;
    }
    public void setFirst(T first) {
        this.first = first;
    }
    public void setSecond(T second) {
        this.second = second;
    }
}

Pair原始类型:

public class Pair {
    private Object first;
    private Object second;

    public Pair() {
    }
    public Pair(Object first, Object second) {
        this.first = first;
        this.second = second;
    }

    public Object getFirst() {
        return first;
    }
    public Object getSecond() {
        return second;
    }
    public void setFirst(Object first) {
        this.first = first;
    }
    public void setSecond(Object second) {
        this.second = second;
    }
}

Interval泛型类型:

import java.io.Serializable;

public class Interval<T extends Comparable & Serializable> implements Serializable {
    private T lower;
    private T upper;
    
    public Interval(T first, T second){
        if (first.compareTo(second) <= 0){
            lower = first;
            upper = second;
        }
        else {
            lower = second;
            upper = first;
        }
    }
}

Interval原始类型:

import java.io.Serializable;

public class Interval implements Serializable {
    private Comparable lower;
    private Comparable upper;
    
    public Interval(Comparable first, Comparable second){
        if (first.compareTo(second) <= 0){
            lower = first;
            upper = second;
        }
        else {
            lower = second;
            upper = first;
        }
    }
}

2. 翻译泛型表达式

当程序调用泛型方法,如果擦除返回类型,编译器插入强制类型转换。

	Pair<Employee> buddies = ...;
	Employee buddy = buddies.getFirst();

擦除类型后将返回Object类型。编译器自动插入Employee的强制类型转换。

3. 翻译泛型方法

泛型方法:

	public static <T extends Comparable> T min(T[] a) {}

擦除后:

	public static Comparable min(Comparable[] a) {}

类型擦除与多态发生了冲突,需要在编译器在类中生成一个桥方法。

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

4. 约束与局限性

  1. 不能用基本类型实例化类型参数,即没有Pair,只有Pair;
  2. 运行时类型查询只适用于原始类型,即不能a instanceof Pair;
  3. 不能创建参数化类型的数组,不能创建这样的数组Pair[] table = new pair[10],可以声明Pair[];
  4. Varargs(可变参数)警告;
  5. 不能实例化类型变量,不能使用new T(),new T[]或T.class这样的表达式中的类型变量。
  6. 不能构造泛型数组
  7. 泛型类的静态上下文中类型变量无效;
  8. 不能抛出或捕获泛型类的实例;
  9. 可以消除对受查异常的检查;
  10. 注意擦除后的冲突;

5. 泛型类型的继承规则

Pair不是Pair的子类,他们之间没有联系,他们都是Pair的子类;

五、通配符类型

1. 概念

通配符类型中,允许类型参数变化,如:Pair<? extends Employee>表示任何泛型的Pair类型,它的类型参数时Employee子类。

2. 通配符的超类型限定

? super Manager这个通配符限制为Manager的所有超类型。
带有超类型限定的通配符可以向泛型对象写入,带有子类型限定的通配符可以从泛型对象读取。

3. 无限定通配符

Pair<?>和Pair的本质不同在于:可以用任意的Object对象调用原始Pair类的setObject方法。

4. 通配符捕获

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);
    }

5. 实例

Pair类:

package pair3;

public class Pair<T> {
    private T first;
    private T second;

    public Pair() {
    }
    public Pair(T first, T second) {
        this.first = first;
        this.second = second;
    }

    public T getFirst() {
        return first;
    }
    public T getSecond() {
        return second;
    }
    public void setFirst(T first) {
        this.first = first;
    }
    public void setSecond(T second) {
        this.second = second;
    }
}

PairAlg类:

package pair3;

public 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);
    }
}

测试:

package pair3;

import test.Employee;
import test.Manager;

public class PairTest {
    public static void main(String[] args) {
        Manager ceo = new Manager("Siri", 8000);
        Manager cfo = new Manager("Xiao", 6000);
        Pair<Manager> buddies = new Pair<>(ceo, cfo);
        printBuddies(buddies);

        Manager[] managers = {ceo, cfo};

        Pair<Employee> result = new Pair<>();
        minmaxSalary(managers, result);
        System.out.println("first: " + result.getFirst().getName() + ", second: " + result.getSecond().getName());
        maxminSalary(managers, result);
        System.out.println("first: " + result.getFirst().getName() + ", second: " + result.getSecond().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 minmaxSalary(Manager[] a, Pair<? super Manager> result) {
        if (a.length == 0)
            return;
        Manager min = a[0];
        Manager max = a[0];
        for (int i = 1; i < a.length; i++){
            if (min.getSalary() > a[i].getSalary())
                min = a[i];
            if (max.getSalary() < a[i].getSalary())
                max = a[i];
        }
        result.setFirst(min);
        result.setSecond(max);
    }

    public static void maxminSalary(Manager[] a, Pair<? super Manager> result) {
        minmaxSalary(a, result);
        PairAlg.swapHelper(result);
    }
}

运行结果:

Siri and Xiao are buddies.
first: Xiao, second: Siri
first: Siri, second: Xiao
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值