6

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

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)而不是使用像这样的一些技巧:

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 in

L extends List<? extends T> & Comparable

List<? extends T>


那么为什么我不能以这种方式调用该方法呢?

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

3 回答 3

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

于 2013-07-23T14:45:37.000 回答
1

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

编辑:快速反映打印输出证实了我的假设。我命名了这些方法method(创意,不是吗?)并得到以下输出:

... method(java.util.List)
... method(java.lang.Iterable)

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

于 2013-07-23T14:12:28.890 回答
1

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

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) 通过允许未经检查的转换使其成为可能。未经检查的转换可以将原始转换FooFoo<..>(但不再像 那样复杂Foo -> Foo<..>&Bar)。

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

于 2013-07-23T15:25:46.137 回答