Java 无法使用URLClassLoader和反射强制转换到接口类

Java 无法使用URLClassLoader和反射强制转换到接口类,java,Java,我有一个罐子: /home/cole/lib/a.jar 在这个jar中,我有以下接口/类(可怕的名称仅用于说明!): CreatorInterface.java Base.java(实现CreatorInterface.java) AbstractBase.java(扩展Base.java) Implementation.java(扩展AbstractBase.java) 在一个单独的项目中,我有以下代码: final URL[] jars = new URL[] { new File

我有一个罐子:

/home/cole/lib/a.jar

在这个jar中,我有以下接口/类(可怕的名称仅用于说明!):

  • CreatorInterface.java
  • Base.java(实现CreatorInterface.java)
  • AbstractBase.java(扩展Base.java)
  • Implementation.java(扩展AbstractBase.java)
在一个单独的项目中,我有以下代码:

final URL[] jars = new URL[] {
  new File("/home/cole/lib/a.jar").toURL();
}

final URLClassLoader classLoader = new URLClassLoader(jars, null);
final Class<?> implementation = classLoader.loadClass("Implementation");
final CreatorInterface object = (CreatorInterface)implementation.newInstance();
更新2

正如@davidxxx所回答的,我有两个接口类(一个在jar中,一个在使用它的项目中)。虽然接口是相同的,但这是问题的原因

然而,为了使其正常工作,我需要像这样修复我的URLClassLoader,以避免出现
ClassNotFoundException

final ClassLoader parent = this.getClass().getClassLoader();
final URLClassLoader classLoader = new URLClassLoader(jars, parent);
此例外情况:

java.lang.ClassCastException:无法将实现强制转换为 创作者界面

让我觉得您很可能有两个不同的
CreatorInterface
类:一个包含在jar中,另一个来自试图加载它的客户端程序。
即使这两个类具有相同的名称(限定名称),对于每个类加载器,它们也是不同的类,因为这里您使用两个未关联的类加载器。
您拥有所运行程序的当前类加载器,以及指定为父类加载器的另一个类加载器
null

final URLClassLoader classLoader = new URLClassLoader(jars, null);
因此,当您尝试将反射创建的对象分配给
CreatorInterface
变量时,强制转换失败,因为每个类加载器加载并使用两个不同的
CreatorInterface
:一个来自客户端代码的类加载器,另一个来自实例化的类加载器。
使用单个类加载器可以解决这个问题,但最佳实践是将jar包含在项目的类路径中,并确保jar中提供的类只有一个版本

要使事情解耦,您可能应该将jar拆分为两个jar:一个只包含接口的API jar和一个依赖于API jar并包含其他类的实现jar。
在客户机代码中,只在类路径中添加API jar,以便能够分配给接口声明类型的变量


关于你的第二点:

这不是关于使用URLClassLoader的问题,类被发现是正确的,问题似乎在实例化中。例如 以下代码可以正常工作:

final Object object = implementation.newInstance();
在这种情况下,您不需要引用接口类型。
实际上,您将
实现
对象分配给
对象
,而不是
创建者界面
变量。
正确/一致的接口和子类由类加载器加载,但在这里,您永远不会有机会引发
ClassCastException
,因为您从未将其分配给重复类的类型,而是一次定义的
Object

因此,以前遇到的问题不会发生


关于第三点:

但是为了让它工作,我需要像这样修复我的URLClassLoader, 要避免ClassNotFoundException,请执行以下操作:

它之所以有效,是因为您在这里创建了一个与父类加载器关联的类加载器

事实上,如果你这样做了:

final URLClassLoader classLoader = new URLClassLoader(jars);

它将产生与创建的
URLClassLoader
对象相同的结果,默认情况下,该对象将委托给父类加载器(这里是启动应用程序的类加载器)

更新的问题可能重复,不要这样认为。类被发现很好,我可以很好地实例化它,但我不能强制转换它。如果接口是跨多个项目共享的,为什么您的jar中会包含接口?当接口实际位于jar中时,您的单独项目如何知道它?接下来,用两个不同的对等类加载器加载同一个类将导致两个不同的类,它们实际上无法来回转换,这可能就是您在这里看到的。可能是重复的,谢谢!你完全正确,但我也发现了代码中的另一个问题。更新了原始答案以反映这一点。@imrichardcole有趣的观点。我错过了一些东西。我更新了,以便更准确。
final ClassLoader parent = this.getClass().getClassLoader();
final URLClassLoader classLoader = new URLClassLoader(jars, parent);
final URLClassLoader classLoader = new URLClassLoader(jars);