泛型方法

@author LeslieTong
@data 2017-11-20


Stay hungry, stay foolish. ——Steve·Jobs


定义泛型方法

假设需要实现一个方法——该方法负责将一个Object数组的所有元素添加到一个Collection集合中。
考虑采用如下代码来实现该方法。

static void fromArrayToCollection(Object[] a, Collection<Object> c) {
    for (Object o: a) {
        c.add(o);
    }
}

上面定义的方法没有任何问题,关键在于方法中的c形参,它的数据类型是Collection。
正如前面所介绍的,Collection< String >不是Collection< Object >的子类型——所以这个方法的功能非常有限,它只能将Object数组的元素复制到Object(Object的子类不行)Collection集合中,
即下面代码将引起编译错误。

String[] strArr = {"a", "b"};
List<String> strList = new ArrayList<>();
// Collection<String>对象不能当成Collection<Object>使用,下面代码出现编译错误
fromArrayToCollection(strArr, strList);

可见上面方法的参数类型不可以使用Collection< String >,那使用通配符Collection

修饰符 <T, S> 返回值类型 方法名(形参列表) {
    // 方法体
}

于是可以把上面的fromArrayToCollection方法改为如下格式:

static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
    for (T o : a) {
        c.add(o);
    }
}

下面程序示范了完整的用法。

public class GenericMethodTest {

    // 声明一个泛型方法,该泛型方法中带一个T类型形参
    static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
            for (T o  : a) {
                c.add(o);
            }
    }           

    public static void main(String[] args) {
        Object[] oa = new Object[100];
        Collection<Object> co = new ArrayList<>();
        // 下面代码T代表Object类型
        fromArrayToCollection(oa, co);
        String[] sa = new String[100];
        Collection<String> cs = new ArrayList<>();
        // 下面代码T代表String类型 
        fromArrayToCollection(sa, cs);
        // 下面代码中T代表Object类型
        fromArrayToCollection(sa, co);
        Integer[] ia = new Integer[100];
        Float[] fa = new Float[100];
        Number[] na = new Number[100];
        Collection<Number> cn = new ArrayList<>();
        // 下面代码中T代表Number类型
        fromArrayToCollection(ia, cn);
        // 下面代码中T代表Number类型
        fromArrayToCollection(fa, cn);
        // 下面代码中T代表Number类型
        fromArrayToCollection(na, cn);
        // 下面代码中T代表Object类型
        fromArrayToCollection(na, co);
        // 下面代码中T代表String类型,但na是一个 Number数组
        // 因为Number既不是String类型,也不是它的子类
        // 所以出现编译错误
        // fromArrayToCollection(na, cs);
    }

}

为了让编译器能准确推断出泛型方法中类型形参的类型 ,不要制造迷惑。

一旦系统迷惑了,就是你错了! ——李刚

看如下程序

public class ErrorTest {

    // 声明一个泛型方法,该泛型方法中带一个T类型参数
    static <T> void test(Collection<T> from, Collection<T> to) {
        for (T ele : from) {
            to.add(ele);
        }
    }

    public static void main(String[] args) {
        List<Object> as = new ArrayList<>();
        List<String> ao = new ArrayList<>();
        // 下面代码将产生编译错误
        //test(as, ao);
    }

}

上面程序定义了test方法,该方法用于将前一个集合里的元素复制到下一个集合中,该方法中的两个形参from、to的类型都是Collection,这要求调用该方法时的两个集合实参中的泛型类型相同,否则编译器无法准确推断出泛型方法中类型形参的类型。

上面程序中调用test方法传入了两个实际参数,其中as的数据类型是List,而ao的数据类型是List,与泛型方法签名进行对比:test(Collection< T > a, Collection< T> c),编译器无法正确识别T所代表的实际类型。为了避免这种错误,可以将该方法改为如下形式 :

public class RightTest {

    // 声明一个泛型方法,该泛型方法中带一个T类型参数
        static <T> void test(Collection<? extends T> from, Collection<T> to) {
            for (T ele : from) {
                to.add(ele);
            }
        }

    public static void main(String[] args) {
        List<Object> ao = new ArrayList<>();
        List<String> as = new ArrayList<>();
        // 下面代码正常
        test(as, ao);
    }

}

上面代码改变了test方法签名,将该方法的前一个形参类型改为Collection< ? extends T>,这种采用类型通配符的表示方式,只要test方法的前一个Collection集合里的元素类型是后一个Collection集合里元素类型的子类即可。

泛型方法和类型通配符的区别

大多数时候都可以用泛型方法代替类型通配符。例如,对于Collection接口中两个方法定义:

public interface Collection<E> {
    boolean containsAll(Collection<?> c);
    boolean addAll(Collection<? extends E> c);
    ...
}

我们看到上面两个方法都使用了类型通配符的形式,下面我们用泛型方法的形式:

public interface Collection<E> {
    boolean <T> containAll(Collection<T> c);
    boolean <T> allAll(Collection<T> c);
    ...
}

上面方法使用了< T extends E >泛型形式,这时定义类型形参时设定上限。其中E是Collection接口里定义的类型形参,在该接口里E可当成普通类型使用。

设定通配符下限

Java允许设定通配符的下限:\< ? super Type>,这个通配符表示它必须是Type本身或者Type的父类。代码如下:

public class MyUtils {

    // 下面des集合元素的类型必须与src集合元素的类型相同或者是其父类
    public static <T> T copy (Collection<? super T> des, Collection<T> src) {
        T last = null;
        for (T ele : src) {
            last = ele;
            des.add(ele);
        }
        return last;
    }

    public static void main(String[] args) {
        List<Number> ln = new ArrayList<>();
        List<Integer> li = new ArrayList<>();
        li.add(5);
        // 此处可准确知道最后一个被复制的元素是Integer类型
        // 与src集合元素类型相同
        Integer last = copy(ln, li);
        System.out.println(ln);
    }

}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值