SBT 中 运行 代码时的 ClassCastException
ClassCastException when running code in SBT
我的代码大致如下所示:
val classLoader = new URLClassLoader(entry.jars, Thread.currentThread.getContextClassLoader)
val env = classLoader.loadClass(entry.name).asSubclass(classOf[Environment])
对于上下文:"entry" 是 JAR 文件和 class 名称的集合。 class 预计会延长 Environment
。 Environment
在同一个包中定义,不使用任何 class 加载技巧。 JAR 文件集合仅包含指定 class 所需的 classes,仅此而已(尤其是任何父 ClassLoader
已经加载的内容)。
当我 运行 sbt 中的这段代码和 run
时,一切正常。但是如果我在同一个 sbt shell 中第二次 运行 它,我得到:
[error] (run-main-1) java.lang.ClassCastException: class edu.tum.cs.isabelle.impl.Environment
java.lang.ClassCastException: class edu.tum.cs.isabelle.impl.Environment
at java.lang.Class.asSubclass(Class.java:3404)
我不明白这里的问题:在 运行 之间没有重新编译或任何东西。我为 test
执行了类似的代码,并且在那里工作正常。当我将 fork
设置为 true
时它确实有效,但我没有理由必须这样做。
为了调试,我检查了 getClassLoader
的 classOf[Environment]
和加载的 class 的超级 class。果然,在第一个运行中,它们是等价的,而在第二个运行中,它们是等价的。不过,他们的 toString
表示是相同的。
编辑: 更多调试。我已经打印出所有涉及的 classloaders:
的 System.identityHashCode
println(s"self: " + System.identityHashCode(classOf[Environment].getClassLoader))
println(s"fresh: " + System.identityHashCode(classLoader))
println(s"impl: " + System.identityHashCode(classLoader.loadClass(entry.name).getClassLoader))
println(s"impl.parent: " + identityHashCode(classLoader.loadClass(entry.name).getSuperclass.getClassLoader))
第一个运行的结果:
self: 1209891953
fresh: 1734438968
impl: 1734438968
impl.parent: 1209891953
第二个结果运行:
self: 1952422714
fresh: 1110295313
impl: 1110295313
impl.parent: 1209891953
Rob Norris 在别处提出了解决方案。由于当前Thread的classloader可以是"literally anything",所以我应该确保使用用来加载Environment
的classloader。果然,如果我把代码改成
val classLoader = new URLClassLoader(entry.jars, getClass.getClassLoader)
代码按预期运行。
我的代码大致如下所示:
val classLoader = new URLClassLoader(entry.jars, Thread.currentThread.getContextClassLoader)
val env = classLoader.loadClass(entry.name).asSubclass(classOf[Environment])
对于上下文:"entry" 是 JAR 文件和 class 名称的集合。 class 预计会延长 Environment
。 Environment
在同一个包中定义,不使用任何 class 加载技巧。 JAR 文件集合仅包含指定 class 所需的 classes,仅此而已(尤其是任何父 ClassLoader
已经加载的内容)。
当我 运行 sbt 中的这段代码和 run
时,一切正常。但是如果我在同一个 sbt shell 中第二次 运行 它,我得到:
[error] (run-main-1) java.lang.ClassCastException: class edu.tum.cs.isabelle.impl.Environment
java.lang.ClassCastException: class edu.tum.cs.isabelle.impl.Environment
at java.lang.Class.asSubclass(Class.java:3404)
我不明白这里的问题:在 运行 之间没有重新编译或任何东西。我为 test
执行了类似的代码,并且在那里工作正常。当我将 fork
设置为 true
时它确实有效,但我没有理由必须这样做。
为了调试,我检查了 getClassLoader
的 classOf[Environment]
和加载的 class 的超级 class。果然,在第一个运行中,它们是等价的,而在第二个运行中,它们是等价的。不过,他们的 toString
表示是相同的。
编辑: 更多调试。我已经打印出所有涉及的 classloaders:
的System.identityHashCode
println(s"self: " + System.identityHashCode(classOf[Environment].getClassLoader))
println(s"fresh: " + System.identityHashCode(classLoader))
println(s"impl: " + System.identityHashCode(classLoader.loadClass(entry.name).getClassLoader))
println(s"impl.parent: " + identityHashCode(classLoader.loadClass(entry.name).getSuperclass.getClassLoader))
第一个运行的结果:
self: 1209891953
fresh: 1734438968
impl: 1734438968
impl.parent: 1209891953
第二个结果运行:
self: 1952422714
fresh: 1110295313
impl: 1110295313
impl.parent: 1209891953
Rob Norris 在别处提出了解决方案。由于当前Thread的classloader可以是"literally anything",所以我应该确保使用用来加载Environment
的classloader。果然,如果我把代码改成
val classLoader = new URLClassLoader(entry.jars, getClass.getClassLoader)
代码按预期运行。