泛型与继承

类型变量的限定

有时,类或方法需要对类型变量加以约束。下面是一个典型的例子,我们要计算数组中的最小元素:

class ArrayListUnit
{
    public static <T> T t (T ...args)
    {
        T item = args[0];

        for (int i=1; i < args.length; i++)
          if (item.compareTo(args[i]) > 0)
            item = args[i];

        return item;
    }
}

该代码会编译失败,这里有个问题,变量args类型为T,这意味着它可以是任何一个类的对象。如何知道T所属的类有一个compareTo方法呢?

解决这个问题的方法是限制T只能是实现了Comparator接口的类。可以通过对类型变量做一个限定:

public static <T extends Comparable<T> T t (T ...args)

注意,这里使用的是extends关键字,而不是implements。

<T extends BoundingType>表示T应该是限定类型(boundingType)的子类型(subtype)。T和限定类可以是类,也可以是接口。

一个类型变量或通配符可以有多个限定,例如:

T extends Comparable & Clonable

限定类型用&隔开,而逗号用来分割类型变量。

<T extends Object, U extends Comparable>

在java的继承中,可以根据需要拥有多个接口超类型,但最多有一个限定可以是类,如果有一个类作为限定,它必须是限定列表中的第一个。

最后,来改写上述方法

package com.studyjava.demo;

import java.util.*;

public class Demo19
{
    public static void main (String[] args)
    {       
        System.out.println(ArrayListUnit.min(3,23,12));
        System.out.println(ArrayListUnit.min("php", "java", "c++", "golang", "c#"));
    }
}

class ArrayListUnit
{
    public static <T> ArrayList<T> arrayListAdd (T ...elements)
    {
        ArrayList<T> ts = new ArrayList<>();

        for (T e : elements) {
            ts.add(e);
        }

        return ts;
    }

    public static <T extends Comparable<T>> T min (T ...args)
    {
        T item = args[0];

        for (int i=1; i < args.length; i++)
            if (item.compareTo(args[i]) > 0)
                item = args[i];

        return item;
    }
}

泛型在继承中的体现

如果B是A的一个子类型(子类或者子接口),而G是具有泛型声明的 类或接口,G并不是G的子类型!

比如:String是Object的子类,但是List并不是List 的子类。

QQ截图20210311163156 - 泛型与继承

下面,我们用一段代码来验证上述结论:

class Util
{
    public static void show (ArrayList<Object> list)
    {
        for (Object o : list) {
            System.out.println(o);
        }
    }
}
ArrayList<Object> objects = new ArrayList<>();
objects.add(23);
objects.add("hello");
objects.add(23.1);

ArrayList<String> strings = new ArrayList<>();
strings.add("hello");
strings.add("java");
strings.add("&php");

Util.show(objects);  // ok
Util.show(strings);  // error

如果ArrayList时ArrayList的子类型,那么上述测试代码就不会报错。正因为他们之间没有继承关系,所以上述代码出错了。