调用其参数以交集类型为界的方法

时间:2013-07-23 13:47:20

标签: java generics

静态方法的上下文中,我想缩小类型引用,并为这样的对象调用更具体的方法:

public static <T, L extends List<? extends T> & RandomAccess> void do(L list) {
    // Do some stuff
}

public static <T> void do(Iterable<? extends T> iterable) {
    if(iterable instanceof List && iterable instanceof RandomAccess)
        // invoke do(List&RandomAccess) method
    else
        // do something else
}


所以我想知道是否有语法允许调用 do(Iterable),而不是使用像这样的一些hack:

private static <L extends List<? extends T> & Comparable> L cast(Iterable<? extends T> iterable) {
    return (L) iterable;
}

public static <T> void do(Iterable<? extends T> iterable) {
    if(iterable instanceof List && iterable instanceof RandomAccess)
        do(cast(iterable));
    else
        // do something else


注意: 我知道不可能以这种方式投射我的可迭代

do((List<? extends T> & RandomAccess) iterable);

中找到L的擦除
L extends List<? extends T> & Comparable

List<? extends T>


那么为什么我不能这样调用这个方法呢?

do((List<? extends T>) iterable); // Which results in invoking do(Iterable<? extends T)

3 个答案:

答案 0 :(得分:2)

您的假设是正确的:交叉点的擦除是第一个类型 - 即List(用于反射等目的)。

通过提供要作为方法类型转换的交集类型,您可以在没有“hack”的情况下编译代码:

public static <T, L extends List<? extends T> & RandomAccess> void method(L list) {
    // do whatever
}

@SuppressWarnings("unchecked") // needed to suppress unsafe cast warning 
public static <T, L extends List<? extends T> & RandomAccess> void method(Iterable<? extends T> iterable) {
    if(iterable instanceof List && iterable instanceof RandomAccess)
        method((L)iterable); // calls the other method
    else
        return; // do whatever
}

此代码编译,第二种方法根据需要调用第一种方法。

没有这种技术就没有办法施放到十字路口。


请注意,您的代码无法编译,因为do是一个java关键字,因此不是有效的方法名称。我使用method()来获得可编译的示例。

此外,我认为您的RandomAccess代表Comparable。{/ p>

答案 1 :(得分:1)

AFAIK L extends List<? extends T> & RandomAccess的删除将是List,但即使我错了,你也不能使用(List<? extends T>) iterable,因为那不符合要求({{1} }}不一定是List)。因此,唯一匹配的方法是RandomAccess版本。

编辑:快速反映打印输出确认了我的假设。我将方法命名为Iterable(创意,不是吗?)并获得以下输出:

method

您现在可能认为转换为... method(java.util.List) ... method(java.lang.Iterable) 就足够了(如果您禁用泛型或通过反射调用方法,则可能是这样),但编译器不会遭受类型擦除,因此知道只有List匹配。

答案 2 :(得分:1)

我认为没有办法调用 static 方法的擦除版本(在干净的构建中)

public static <L extends List<?> & RandomAccess> void do1(L list) {}

private static <L extends List<?> & RandomAccess> L cast(Iterable<?> iterable) 
{
    return (L) iterable;
}

public static void do2(Iterable<?> iterable)
{
    do1(cast(iterable));

    // try to invoke the erased version
    //do1((List)iterable);   // does not compile
}

我们可以通过原始类型

调用实例方法的擦除版本
static class Helper<T>
{
    <T, L extends List<? extends T> & RandomAccess> void do3(L list)
    {
        do1(list);
    }
}

public static void do2(Iterable<?> iterable)
{
    Helper helper = new Helper(); // raw type
    helper.do3((List) iterable);  // erased method
}

返回静态方法。假设我们有这个Java5之前的代码

#1 static void foo(List list){}

#2 foo(new ArrayList());

现在在Java5之后,#1被广泛化为

#1 static void foo(List<?> list){}

但#2保持不变,原始类型。为什么#2仍在编译?规范(15.12.2.2)允许进行未经检查的转换。未经检查的转化可以将原始Foo转换为Foo<..>(但不再像Foo -> Foo<..>&Bar那样复杂。)

规范只包含足够的技巧来确保遗留代码能够在典型的泛化中存活下来。你的情况肯定不典型,并且不适用这些技巧。