列表初始化时出现奇怪的错误

10

一个朋友问我是否能帮助他找出代码中错误的原因,更重要的是当他添加一些代码时为什么错误会消失。我查看了关于类的文档,但也找不到原因。

这是代码:

import java.util.Arrays;
import java.util.List;

import javax.swing.JComponent;
import javax.swing.JPanel;
import javax.swing.JTabbedPane;

public class Test {

    public static void main(String[] args) {
        /**
        * This line shows this compilation error in eclipse:
        * Type mismatch: cannot convert from 
        *     List<Class<? extends JComponent&Accessible>> 
        *          to List<Class<? extends JComponent>>
        */
        List<Class<? extends JComponent>> listComp = Arrays.asList(JTabbedPane.class, 
                                                                   JPanel.class);

        /**
        * This one compiles fine and the difference is that
        * he added JComponent.class on the list and the 
        * code is working fine
        */
        List<Class<? extends JComponent>> listComp2 = Arrays.asList(JTabbedPane.class, 
                                                                    JPanel.class, 
                                                                    JComponent.class);
    }

}

正如您所看到的,唯一的区别在于第二个列表变量中我们添加了JComponent.class,错误消失了。

这是为什么呢?

更新

我们正在使用Java JDK 7 update 80

更新2

另一个朋友建议了这段代码,它也可以工作:

List<Class<? extends JComponent>> listComp = 
      new ArrayList<Class<? extends JComponent>> ( Arrays.asList( JTabbedPane.class, 
                                                                  JPanel.class));

2
在我的IntelliJ与Java 8下,这两个都可以很好的编译。 - Mureinik
可能是JDK 7上的某种错误?O.O - Jorge Campos
2
Java 8 中的类型推断已经发生了变化,答案可能在 jls 的这个部分 中。 - Alex - GlassEditor.com
1个回答

8

我可以在Java 7中复制此问题,但当我切换到Java 8时,错误不再出现。此外,如果我仍然使用Java 7,但为Arrays.asList提供类型参数,则不再出现错误。

List<Class<? extends JComponent>> listComp =
    Arrays.<Class<? extends JComponent>>asList(JTabbedPane.class, JPanel.class);

Java会尝试确定Arrays.asList返回的目标类型。显然,JTabbedPaneJPanel都扩展了JComponent并实现了Accessible,但是JComponent没有实现Accessible。结果是编译器可以从所有类型中获得的最完整、最具体的类型。对于第一个示例,推断的类型是List<Class<? extends JComponent & Accessible>>,因为两个参数都符合该类型。当你添加JComponent时,推断的类型现在是List<Class<? extends JComponent>>,与listComp相匹配,使其能够在Java 7中编译。
Java 8具有改进的目标类型推断功能。该教程的示例是将Collections.emptyList传递给需要List<String>的方法。在Java 7中,推断的类型将是List<Object>,并且将出现编译错误。但是,在Java 8中,推断的类型是List<String>,与方法的参数匹配,代码编译通过。
虽然这不是您代码的完全相同的示例,但足够接近,我相信总的来说,Java 8改进的目标类型推断解释了为什么该代码在该版本中编译。

这就是问题所在:“结果是编译器可以从所有类型中共同获得的最完整、最具体的类型”。谢谢,解释得非常好。 - Jorge Campos
1
这也应该可以工作:... = Arrays.asList((JComponent) JTabbedPane.class, JPanel.class); - Stephen C
嗨@StephenC,你在使用Java 8吗?我这里用的是Java 7更新80,使用你的代码出现了以下错误:类型不匹配: 无法将List<Serializable>转换为List<Class<? extends JComponent>> - Jorge Campos

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接