05_generic_02_class

第五十八章 Java泛型类(Java Generic Classes)

1 简介

在Java泛型编程中,类型参数也可以用于类声明上,将类的定义泛型化。一个泛型的类又被称为带参数的类型(Parameterized Type)。如下面的代码所示,我们可以在类名后面添加一对尖括号,表示该类是一个泛型类。类型变量放在尖括号中。所以,下面的代码可以用于求两个整数Integer的较大值或者两个浮点Double对象的较大值。

public class GenericClassExample<T> {
    public T max(T a, T b) {
        ...
    }
}

2 实例化

既然泛型类也是类,就可以实例化。与普通类实例化不同的是,在实例化泛型类时,需要指定类型参数的真实类型。例如,在如下代码中,分别使用Integer和Double实例化了GenericClassExample<T>的两个对象。

public class GenericClassExample<T> {
    public static void main(String[] args) {
        GenericClassExample<Integer> i = new GenericClassExample<Integer>();
        GenericClassExample<Double> d = new GenericClassExample<Double>();
        ...
    }
}

3 继承

泛型类也能和普通类一样使用类继承特性。例如,下面的代码展示了三种不同的继承场景。在第一个例子中,DerivedGenericClassExample1是一个泛型类。它继承自一个普通类Student。第二个例子也是一个泛型类继承自普通类的例子。虽然GenericClassExample是一个泛型类,但是我们给出了具体的类型Integer。所以,实际上,DerivedGenericClassExample2<T>继承自GenericClassExample<Integer>。参数类型T只应用于子类DerivedGenericClassExample2中。在第三个例子中,父类和子类都是泛型类,它们的参数类型相同,都是T。

public class DerivedGenericClassExample1<T> extends Student {
    
}

public class DerivedGenericClassExample2<T> extends GenericClassExample<Integer> {
    
}

public class DerivedGenericClassExample3<T> extends GenericClassExample<T> {
    
}

但是,泛型类的继承关系有时也不太容易理解。例如,在下面的例子中,Freshman类继承自Student类。所以,Freshman和Student之间有着继承关系。但是,当我们将其用于泛型类的类型参数后,得到的类之间不存在任何关系。它们是两个独立的类。例如:GenericClassExample<Freshman>和GenericClassExample<Student>是两个独立的类。它们之间不存在继承关系。

public class Student {
}

public class Freshman extends Student {
}

4 使用通配符(Wildcard Types)

与泛型方法类似的是,我们在声明一个泛型类时,同样可以为类型参数设置一个上界。我们同样也使用关键字extends表明继承关系。

public class GenericClassExample<T extends Comparable> {
    public T max(T a, T b) {
        ...
    }
}

当我们声明一个GenericClassExample<T>类型的变量时,我们也可以给类型参数T一个上界。例如:print成员方法只接收一个类型为GenericClassExample<T>的参数,并且类型T必须继承自BigInteger类。在这里,通配符?表示的是任何类,关键字extends表示的是这个类必须继承自BigInteger。

public class PrintBigInteger {
    public void print (GenericClassExample<? extends BigInteger> p) {
        System.out.println(p.toString());
    }
}

另外,当我们使用泛型类型变量时,我们还可以为其设置一个下界。例如,当我们希望接收一个BigInteger的父类作为类型参数的泛型对象时,我们可以使用如下代码。在这里,通配符?表示的是任何类,关键字super表示的是这个类必须是BigInteger的父类。

public class PrintGenericBigInteger {
    public void print (GenericClassExample<? super BigInteger> p) {
        System.out.println(p.toString());
    }
}

当然,如果我们不使用extends或者super,那么类型参数的类型可以是任何类。

public class PrintGenericOfAnyType {
    public void print (GenericClassExample<?> p) {
        System.out.println(p.toString());
    }
}

5 小结

我们在本章介绍了泛型类的基本概念和使用方法。泛型类是一个较为复杂的概念,我们无法在一篇文章中涵盖其所有的内容。但是,我们相信当读者阅读了下一篇文章类型擦除之后,如果能站在Java设计者的角度和Java编译器/虚拟机的角度来理解Java语言的泛型系统的话,泛型编程能够帮助每一位开发人员构建功能更完善、丰富、扩展性更好的应用程序和代码库。

上一章
下一章

注册用户登陆后可留言

Copyright  2019 Little Waterdrop, LLC. All Rights Reserved.