Foo无法转换为Foo。

9
我正在遍历 ArrayList<Bar>Foo 扩展了 Bar。 我尝试在列表中找到 Foo 的一个实例。
ArrayList<Bar> list = new ArrayList<Bar>();

// fill list with items that are instances of Foo and Bar

for (int i = 0; i < list.size(); i++)
{
  Bar bar = list.get(i);

  if (bar.getClass().getName().equals("com.me.Foo")) // "if(bar instanceof Foo)" never passes
  {
    System.out.println("bar is an instance of Foo");
    Foo foo = (Foo) bar;
  }
}

如果运行代码的这一部分,我会收到以下错误:
java.lang.ClassCastException: com.me.Foo cannot be cast to com.me.Foo
// stack

然而,在另一个地方,我有一个返回Bar实例的方法,我可以使用instanceof检查它是否是Foo实例,然后毫无问题地将Bar转换为Foo
Bar bar = returnBar(); // returns an instance of Bar, but the variable 
                       // it returns could be an instance of Foo
if (bar instanceof Foo)
{
  System.out.println("bar is an instance of Foo");
  Foo foo = (Foo) bar;
}

这里有什么不同?

这是问题的真正来源: http://pastie.org/private/jhyhovn7mm4ghxlxsmweog

我正在使用一个名为Bukkit的API,它允许为名为Minecraft的游戏的服务器创建自定义插件。文档可以在这里找到,对于那些真正感兴趣的人来说,这个问题已经变成了API特定的问题,我将去官方的Bukkit论坛尝试我的问题,但我会把所有这些留在这里,以供那些对问题感兴趣的人参考。


它们都继承自同一个基类吗? - Asdfg
请问您能否提供您的问题的IDEOne演示?http://ideone.com - jmj
我在你的代码中看到了两个类,Foo和Bar。它们都继承自同一个基类吗? - Asdfg
@JigarJoshi 是的,不过我将使用我的真实世界例子。准备好后,我会在主问题中发布。 - Austin Moore
这段代码在Java 1.6上运行成功了。我没想到它会跑起来,但它确实跑了。 - Jim
@Asdfg 啊,谢谢澄清。我不确定你是在谈论 ArrayList 中的变量和 returnBar() 收到的变量,还是类。答案是肯定的。 - Austin Moore
1个回答

19

我猜测你正在从两个地方加载Foo。在你的循环中,尝试打印出bar.getClass().getClassLoader()Foo.class.getClassLoader()。我怀疑它们将显示不同的类加载器。(当然,有可能你有两个类加载器从同一位置加载,这同样糟糕。)

从JVM的角度来看,从不同类加载器加载的具有相同名称的两个类是完全不同的 - 你无法将一个转换为另一个。如果我的猜测是正确的,通常的解决方案是尝试找出为什么它们被从不同的类加载器中加载,并修复它。

请注意,当使用instanceof时,它会检查类型是否真正兼容,因此它会注意到不同的类加载器。


1
似乎可能是一个很好的机会。如果instanceof操作失败,通过名称强制解决问题不太可能修复它,只会帮助揭示潜在问题。我也在一个脏构建中遇到过这个问题,请确保彻底清理了构建过程。如果你有与com.me.Foo冲突的JAR包,也可能发生这种情况。 - PlexQ
我还没有遇到过这种情况,JVM自己选择不同的类加载器可能是什么原因? - jmj
2
@JigarJoshi:这可能是Web应用程序中的一个问题,因为您有来自各种不同类加载器的不同代码运行,这最终变得非常复杂。(通常会发生在库类中-例如,如果您在war文件和容器类加载器中都有log4j。) - Jon Skeet
你是正确的,看起来它们被两个不同的类加载器加载。谢谢你的帮助 :) - Austin Moore

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