Java 强制转换到同一类时出现 ClassCastException

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/826319/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-11 19:56:28  来源:igfitidea点击:

ClassCastException when casting to the same class

javareflectionclasscastexceptionwebsphere-portal

提问by Jaime Garcia

I have 2 different Java projects, one has 2 classes: dynamicbeans.DynamicBean2and dynamic.Validator.

我有 2 个不同的 Java 项目,一个有 2 个类:dynamicbeans.DynamicBean2dynamic.Validator.

On the other project, I load both of these classes dynamically and store them on an Object

在另一个项目中,我动态加载这两个类并将它们存储在 Object

class Form {
    Class beanClass;
    Class validatorClass;
    Validator validator;
}

I then go ahead and create a Validatorobject using validatorClass.newInstance()and store it on validatorthen I create a bean object as well using beanClass.newInstance()and add it to the session.

然后我继续创建一个Validator对象validatorClass.newInstance()并将其存储在上面,validator然后我创建一个 bean 对象beanClass.newInstance()并将其添加到会话中。

portletRequest.setAttribute("DynamicBean2", bean);

During the lifecycle of the Formproject, I call validator.validate()which loads the previously created bean object from the session (I'm running Websphere Portal Server). When I try to cast this object back into a DynamicBean2it fails with a ClassCastException.

Form项目的生命周期中,我调用validator.validate()which 从会话中加载先前创建的 bean 对象(我正在运行 Websphere Portal Server)。当我尝试将此对象强制转换回 时,DynamicBean2它会因 ClassCastException 而失败。

When I pull the object back out of the session using

当我使用

faces.getApplication().createValueBinding("#{DynamicBean2}").getValue(faces);

and check the class of it using .getClass()I get dynamicbeans.DynamicBean2. This is the class I want to cast it to however when I try I get the ClassCastException.

并使用.getClass()I get检查它的类dynamicbeans.DynamicBean2。这是我想将它投射到的类,但是当我尝试时,我得到了 ClassCastException。

Any reason why I'm getting this?

我得到这个的任何原因?

采纳答案by waxwing

I am not quite following your description of the program flow, but usually when you get ClassCastExceptions you cannot explain you have loaded the class with one classloader then try to cast it to the same class loaded by another classloader. This will not work - they are represented by two different Class objects inside the JVM and the cast will fail.

我并没有完全遵循您对程序流程的描述,但是通常当您收到 ClassCastExceptions 时,您无法解释您已经使用一个类加载器加载了该类,然后尝试将其转换为由另一个类加载器加载的同一个类。这将不起作用 - 它们由 JVM 内的两个不同的 Class 对象表示,并且转换将失败。

There is an article about classloading in WebSphere. I cannot say how it applies to your application, but there are a number of possible solutions. I can think of at least:

一篇关于 WebSphere 中的类加载文章。我不能说它如何适用于您的应用程序,但有许多可能的解决方案。我至少能想到:

  1. Change the context class loader manually. Requires that you can actually get a reference to an appropriate class loader, which may not be possible in your case.

    Thread.currentThread().setContextClassLoader(...);
    
  2. Make sure the class is loaded by a class loader higher in the hierarchy.

  3. Serialize and deserialize the object. (Yuck!)

  1. 手动更改上下文类加载器。要求您实际上可以获得对适当类加载器的引用,这在您的情况下可能是不可能的。

    Thread.currentThread().setContextClassLoader(...);
    
  2. 确保该类由层次结构中更高的类加载器加载。

  3. 序列化和反序列化对象。(呸!)

There is probably a more appropriate way for your particular situation though.

不过,对于您的特定情况,可能有更合适的方法。

回答by Robin

The class objects were loaded in different classloaders, therefore the instances created from in each of classes are seen as 'incompatible'. This is a common issue in a an environment where there are many different classloaders being used and objects are being passed around. These issues can easily arise in Java EE and portal environments.

类对象在不同的​​类加载器中加载,因此在每个类中创建的实例被视为“不兼容”。在使用许多不同的类加载器并且正在传递对象的环境中,这是一个常见问题。这些问题很容易在 Java EE 和门户环境中出现。

Casting an instance of a class requires that the Class linked to the object being casted is the same as the one loaded by the current thread context classloader.

转换类的实例要求链接到被转换对象的类与当前线程上下文类加载器加载的类相同。

回答by Emil Lundberg

I got the A2AClassCastException problem when trying to create a List of objects from XML using Apache Commons Digester.

我在尝试使用 Apache Commons Digester 从 XML 创建对象列表时遇到了 A2AClassCastException 问题。

List<MyTemplate> templates = new ArrayList<MyTemplate>();
Digester digester = new Digester();
digester.addObjectCreate("/path/to/template", MyTemplate.class);
digester.addSetNext("/path/to/template", "add");
// Set more rules...
digester.parse(f); // f is a pre-defined File

for(MyTemplate t : templates) { // ClassCastException: Cannot cast mypackage.MyTemplate to mypackage.MyTemplate
    // Do stuff
}

As stated above, the cause is that the digester doesn't use the same ClassLoader as the rest of the program. I ran this in JBoss, and it turned out that commons-digester.jar was not in JBoss's lib directory, but rather in a webapp's lib directory. Copying the jar into mywebapp/WEB-INF/lib also solved the problem. Another solution was to casll digester.setClassLoader(MyTemplate.class.getClassLoader()), but that feels like quite an ugly solution in this context.

如上所述,原因是摘要器没有使用与程序其余部分相同的类加载器。我在 JBoss 中运行了这个,结果发现 commons-digester.jar 不在 JBoss 的 lib 目录中,而是在 webapp 的 lib 目录中。将 jar 复制到 mywebapp/WEB-INF/lib 也解决了问题。另一种解决方案是调用digester.setClassLoader(MyTemplate.class.getClassLoader()),但在这种情况下,这感觉是一个非常丑陋的解决方案。

回答by Andrei

I had the same issue while using several JBoss instances on different machines. To bad I didn't stumble across this post earlier.
There were artifacts deployed on different machines, two of them declared class loaders with identical name.I changed one of the classloader names and everything worked fine => Beware of Copy&Paste!

我在不同的机器上使用多个 JBoss 实例时遇到了同样的问题。糟糕的是,我之前没有偶然发现这篇文章。
在不同的机器上部署了工件,其中两个声明了具有相同名称的类加载器。我更改了一个类加载器名称,一切正常 => 小心复制和粘贴!

Why doesn't the ClassCastException thrown mention the involved class loaders? - I think that would be very useful information.
Does anyone know if there will be anything like this available in the future? Needing to check the class loaders of 20-30 Artifacts is not that pleasant. Or is there something I missed in the exception text?

为什么抛出的 ClassCastException 没有提到所涉及的类加载器?- 我认为这将是非常有用的信息。
有谁知道将来是否会有这样的东西可用?需要检查 20-30 个工件的类加载器并不是那么愉快。或者我在异常文本中遗漏了什么?

EDIT: I edited the META-INF/jboss-app.xml file and changed the name of the loader, the idea is to have a unique name. At work we use the artifact id(unique) combined with the version inserted by maven({$version}) during the build.
Using dynamic fields is only optional but helps if you want to deploy different versions of the same application.

编辑:我编辑了 META-INF/jboss-app.xml 文件并更改了加载程序的名称,想法是要有一个唯一的名称。在工作中,我们将工件 id(unique) 与构建期间由 maven({$version}) 插入的版本结合使用。
使用动态字段只是可选的,但如果您想部署同一应用程序的不同版本,则会有所帮助。

<jboss-app>
   <loader-repository> 
   com.example:archive=unique-archive-name-{$version}
   </loader-repository> 
</jboss-app>

You can find some info here: https://community.jboss.org/wiki/ClassLoadingConfiguration

你可以在这里找到一些信息:https: //community.jboss.org/wiki/ClassLoadingConfiguration

回答by Adrien Dos Reis

I had the same issue, and I finally found a workaround on java.net :

我遇到了同样的问题,我终于在 java.net 上找到了解决方法:

Copy all org.eclipse.persistence jarfiles from glassfish4/glassfish/modulesto WEB-INF/lib. Then go in your glassfish-web.xml, and set class-delegateto false.

将所有org.eclipse.persistence jar文件从复制glassfish4/glassfish/modulesWEB-INF/lib. 然后进入您的glassfish-web.xml, 并设置class-delegatefalse

Worked for me !

为我工作!

回答by John

I had a similar issue with JAXB and JBoss AS 7.1. The issue and solution are described here: javax.xml.bind.JAXBException: Class *** nor any of its super class is known to this context. The exception that was given was org.foo.bar.ValueSet cannot be cast to org.foo.bar.ValueSet

我在 JAXB 和 JBoss AS 7.1 上遇到了类似的问题。此处描述了问题和解决方案:javax.xml.bind.JAXBException: Class *** 或其任何超类都不为该上下文所知。给出的例外是 org.foo.bar.ValueSet 不能转换为 org.foo.bar.ValueSet

回答by Meindert

I had the same issue on a wildfly EJB, The EJB was returning a list of Objects and has an remote and a local interface. I used the Local interface by mistake what was working just fine up until the point you try to cast the objects in the list.

我在 Wildfly EJB 上遇到了同样的问题,EJB 正在返回一个对象列表,并且有一个远程和一个本地接口。我错误地使用了 Local 接口,直到您尝试将对象转换为列表中的对象之前,它一直运行良好。

Local/Remote interface:

本地/远程接口:

public interface DocumentStoreService {

    @javax.ejb.Remote
    interface Remote extends DocumentStoreService {
    }

    @javax.ejb.Local
    interface Local extends DocumentStoreService {
    }

The EJB bean:

EJB bean:

@Stateless
public class DocumentStoreServiceImpl implements DocumentStoreService.Local, DocumentStoreService.Remote {

The correct spring wrapper around the EJB:

EJB 周围正确的弹簧包装器:

<bean id="documentStoreService" class="org.springframework.ejb.access.LocalStatelessSessionProxyFactoryBean">
    <property name="jndiName" value="java:global/dpc/dpc-ejb/DocumentStoreServiceImpl!santam.apps.dpc.service.DocumentStoreService$Remote"/>
    <property name="businessInterface" value="santam.apps.dpc.service.DocumentStoreService$Remote"/>
    <property name="resourceRef" value="true" />
</bean>

Note the $Remote, You can change this to $Local and it will find the Local interface just fine, and also execute the methods without any issue (from a separate application on the same container), but the model objects are not marshaled and are from a different class loader if you use the local interface by mistake.

请注意 $Remote,您可以将其更改为 $Local,它会发现 Local 接口很好,并且还可以毫无问题地执行方法(来自同一容器上的单独应用程序),但模型对象未编组,而是如果您错误地使用本地接口,则从不同的类加载器。

回答by Yogev Levy

Another option:

另外一个选项:

Happened to me in weblogic, but I guess it can happen in other servers as well - if you do (just) "Publish" and therefor some of your classes are re-loaded. Instead do "Clean" so all the classes will re-loaded together.

在 weblogic 中发生在我身上,但我想它也可能发生在其他服务器上 - 如果你(只是)“发布”并且因此你的一些类被重新加载。而是执行“清理”,以便所有类将重新加载在一起。

回答by user3402702

I had same problem with an EJB lookup from another EJB. I solved adding @Remote(MyInterface.class) to EJB class configuration

我在从另一个 EJB 查找 EJB 时遇到了同样的问题。我解决了将@Remote(MyInterface.class) 添加到 EJB 类配置的问题

回答by RAM237

Had the same my.package.MyClass cannot be cast to my.package.MyClasson WildFly 10.1 and, as I understand, I did the opposite to what @Emil Lundberg described in his answer.

有同样my.package.MyClass cannot be cast to my.package.MyClass的WildFly 10.1和,据我所知,我却反其道而行之什么@Emil伦德伯格在他的回答中描述。

I have added the module(which contains my.package.MyClass) to my.war/WEB-INF/jboss-deployment-structure.xmlas a dependency

我已将模块(包含my.package.MyClass)添加my.war/WEB-INF/jboss-deployment-structure.xml为依赖项

<dependencies>
    ...
    <module name="my.package"/>
</dependencies>

and removed the corresponding jarfrom my.war/WEB-INF/lib, re-deployed the WAR and then the code worked as expected.

并从 中删除相应的jarmy.war/WEB-INF/lib,重新部署 WAR,然后代码按预期工作。

Thus, we made sure it solves the issue. Now, we need to make sure the issue won't come back, for example, when the updated version of WARwill be assembled and deployed.

因此,我们确保它解决了这个问题。现在,我们需要确保问题不会再次出现,例如,何时组装和部署更新版本的WAR

For this, in the sources of those WAR, it is required to add <scope>provided</scope>for those jarin pom.xml, so that when my.waris re-assembled next time with the fix/enhancement code injected, it will not bundle this jarinto my.war/WEB-INF/lib.

对于这一点,在这些来源WAR,需要添加<scope>provided</scope>这些pom.xml,从而当my.war是重新组合的下一次带有fix /增强码注入,也不会捆绑此my.war/WEB-INF/lib