Java 带参数的泛型方法与带通配符的非泛型方法

Java 带参数的泛型方法与带通配符的非泛型方法,java,generics,Java,Generics,根据,在某些情况下,泛型方法没有使用通配符类型的等效非泛型方法。根据这个答案, 如果方法签名使用多级通配符类型,则泛型方法签名和通配符版本之间始终存在差异 他们给出了一个方法voidprint1(List),该方法“需要一个相同类型的框列表。”通配符版本为voidprint2(List>x){ 从直觉上看,这些定义应该是等价的。但是,调用f(ArrayList.class)使用第一种方法编译,而调用g(ArrayList.class)使用第二种方法会导致编译时错误: 测试中的g(java.la

根据,在某些情况下,泛型方法没有使用通配符类型的等效非泛型方法。根据这个答案,

如果方法签名使用多级通配符类型,则泛型方法签名和通配符版本之间始终存在差异

他们给出了一个方法
voidprint1(List)
,该方法“需要一个相同类型的框列表。”通配符版本为
voidprint2(List>x){
从直觉上看,这些定义应该是等价的。但是,调用
f(ArrayList.class)
使用第一种方法编译,而调用
g(ArrayList.class)
使用第二种方法会导致编译时错误:

测试中的
g(java.lang.Class>)
无法应用于(java.lang.Class)
有趣的是,这两个函数都可以用彼此的参数调用,因为编译如下:

类测试{

猜猜:表示第一个的东西?(ArrayList)没有“实现”
ArrayList
(借助于双嵌套通配符)。我知道这听起来很有趣,但是

考虑(对于原始列表):

void g(类x){}//失败
void g(类>x){}
无效d(){
ArrayList d=新的ArrayList();
f(d);
g(d);
}
}
这个

//不在g(d)上编译
公开课考试{

如果类型参数是通配符参数化类型,则不会出现问题:

Class<ArrayList<?>> foo = null;
f(foo);
g(foo);
很复杂


我没有仔细阅读语言规范,因此我不确定为什么子类型在显式类型参数情况下有效,但在通配符情况下无效。这也很可能是一个错误。

这些不完全相同:

<T extends Iterable<?>> void f(Class<T> x) {}
void g(Class<? extends Iterable<?>> x) {}
,因此它不匹配


更清楚地说,
g
将接受
Foo实现Iterable
,但不接受
arararylist实现Iterable

好吧,按照规范,两种调用都不合法。但是为什么第一种类型检查而第二种类型不检查呢

不同之处在于如何检查方法的适用性(具体请参见)

  • 为了使简单、非泛型的
    g
    适用,参数
    Class
    ArrayList
    ,writed
    ArrayList

    任何参数化
    C
    都是原始类型
    C
    的一个(直接)子类型。因此
    ArrayList
    ArrayList
    的一个子类型,但不是相反。从传递的角度看,
    ArrayList
    不是
    Iterable
    的子类型

    因此,
    g
    不适用

  • f
    是泛型的,为了简单起见,让我们假设类型参数
    ArrayList
    是显式指定的。要测试
    f
    的适用性,
    Class,这是真的

    同样要使
    f
    适用,类型参数必须是。这不是因为,正如我们上面所示,
    ArrayList
    不是
    Iterable
    的子类型

那么它为什么要编译呢?

这是一个bug。遵循a和JDT编译器明确排除了第一种情况(包含类型参数)。第二种情况仍然被愉快地忽略,因为JDT认为
ArrayList
Iterable
TypeBinding.isCompatibleWith(TypeBinding)
)的子类型


我不知道为什么javac的行为是相同的,但我假设是出于类似的原因。您会注意到,当将原始
ArrayList
分配给
Iterable
时,javac也不会发出未经检查的警告。

+1对于一个没有答案的泛型问题,“您不能对泛型执行此操作”改变一下。语义上的差异是可以理解的,但是我觉得奇怪的是它没有编译。从
g
中删除
Iterable
上的通配符确实会编译,原因如下:
void g(Classic在第二个示例中,为什么调用
f(d)
成功,而调用
g(d)
不编译?与问题中描述的
类进行类比,签名
void g(ArrayList>x)
是否表示
g()
接受Iterable(或Iterable子类)的ArrayList这可能是不同的类型,而
,其中
不是一个集合?我不知道,我会假设,出于同样的原因,您的代码没有编译。我使用了ArrayList两次,以确保它不是特定于类的。如果您愿意,可以将其更改为任何参数化类型,例如box。我的观点是参数化类型似乎在嵌套通配符上消失了,类似于@newacct所提到的。您在这里没有意义。
ArrayList
实现了
Iterable
@PaulBellora正是。
ArrayList
没有实现
Iterable
。请记住,
Iterable
不在就泛型类型而言,
Iterable
。你仍然没有什么意义。关于
ArrayList
实现
Iterable
的一点来自哪里?其次,
Iterable
可以从任何
ArrayList
@PaulBellora-uhh是的。我的意思是
Iterable
(编辑我的答案)但是它仍然坚持认为,
ArrayList
不是匹配项,因为就匹配方法签名而言,
Iterable
不是
Iterable
t
与类相关,但
Iterable
不是-参见我答案的最后一句+1,我一直不清楚为什么g的类文本泛型类是用原始类型而不是通配符类型参数化的。@PaulBellora:有趣的是,这个问题在Scala中不会发生,因为Scala需要m
// Does not compile on g(d)
public class Test{
    <T extends Iterable<?>> void f(ArrayList<T> x) {}
    void g(ArrayList<? extends Iterable<?>> x) {}

    void d(){
        ArrayList<ArrayList> d = new ArrayList<ArrayList>();
        f(d);
        g(d);
    }
}
Class<ArrayList<?>> foo = null;
f(foo);
g(foo);
<T extends Iterable<?>> void f(Class<T> x) {}
void g(Class<? extends Iterable<?>> x) {}