从调用返回另一个代理的Java.lang.reflect.Proxy在赋值时导致ClassCastException
所以我正在玩geotools,我想我会代理他们的一个数据访问类,并跟踪它们在代码中的使用方式。
我编写了一个动态代理并在其中包含了一个FeatureSource(接口),然后快乐地完成了它。 然后我想看一下featureSource返回的一些传递对象,因为FeatureSource做的主要事情是返回一个FeatureCollection(FeatureSource类似于sql DataSource和featurecollection到sql语句)。
在我的invocationhandler中,我只是将调用传递给底层对象,打印出目标类/方法/ args并导致结果,但是对于返回FeatureCollection(另一个接口)的调用,我将该对象包装在我的代理中(相同的类,但一个新的实例,应该不重要吗?)并返回它。 BAM! Classcastexception:
java.lang.ClassCastException: $Proxy5 cannot be cast to org.geotools.feature.FeatureCollection at $Proxy4.getFeatures(Unknown Source) at MyClass.myTestMethod(MyClass.java:295)
调用代码:
FeatureSource featureSource = ... // create the FS featureSource = (FeatureSource) FeatureSourceProxy.newInstance(featureSource, features); featureSource.getBounds();// ok featureSource.getSupportedHints();// ok DefaultQuery query1 = new DefaultQuery(DefaultQuery.ALL); FeatureCollection results = featureSource.getFeatures(query1); //<- explosion here
代理人:
public class FeatureSourceProxy implements java.lang.reflect.InvocationHandler { private Object target; private List features; public static Object newInstance(Object obj, List features) { return java.lang.reflect.Proxy.newProxyInstance( obj.getClass().getClassLoader(), obj.getClass().getInterfaces(), new FeatureSourceProxy(obj, features) ); } private FeatureSourceProxy(Object obj, List features) { this.target = obj; this.features = features; } public Object invoke(Object proxy, Method m, Object[] args)throws Throwable{ Object result = null; try { if("getFeatures".equals(m.getName())){ result = interceptGetFeatures(m, args); } else{ result = m.invoke(target, args); } } catch (Exception e) { throw new RuntimeException("unexpected invocation exception: " + e.getMessage(), e); } return result; } private Object interceptGetFeatures(Method m, Object[] args) throws Exception{ return newInstance(m.invoke(target, args), features); }
}
是否有可能从代理接口动态返回接口代理,或者我做错了什么? 干杯!
Class.getInterfaces()仅返回类实现的DIRECTLY接口。 您需要一个传递闭包来获取所有接口。
UPDATE
例:
private static Class>[] getInterfaces(Class> c) { List> result = new ArrayList>(); if (c.isInterface()) { result.add(c); } else { do { addInterfaces(c, result); c = c.getSuperclass(); } while (c != null); } for (int i = 0; i < result.size(); ++i) { addInterfaces(result.get(i), result); } return result.toArray(new Class>[result.size()]); } private static void addInterfaces(Class> c, List> list) { for (Class> intf: c.getInterfaces()) { if (!list.contains(intf)) { list.add(intf); } } }
您可能还需要“解开”作为参数传递的代理。
@ maurice-perry的解决方案对我很有用,我已经投了赞成票,但我也想指出有所需方法的库实现。
我最终使用Apache Commons库方法ClassUtils.getAllInterfaces()
实现了这个解决方案:
... import org.apache.commons.lang3.ClassUtils; ... private static Class>[] getAllInterfaces(Object object) { final List> interfaces = ClassUtils.getAllInterfaces(object.getClass()); return interfaces.toArray(new Class>[interfaces.size()]); }
它适用于newProxyInstance
中神奇的第二个参数:
Proxy.newProxyInstance(ClassLoader loader, Class>[] interfaces, InvocationHandler h)
还有一种番石榴方法使用:
final Set tt = TypeToken.of(cls).getTypes().interfaces();
但是你必须弄清楚如何将Set
转换为Class>[]
。 也许是微不足道的,如果你是一个番石榴buff,但是Apache已经可以使用了。
这两个都在这个相关的线程中被注意到, 获得了类的所有(派生)接口 。
- Java转换为double to longexception
- 由于ClassLoader问题导致ClassCastException的解决方案
- 签名小程序和服务器端控制器之间的通信
- 如何构建同一个加载不同类加载器的两个实例?
- Javagenerics中不会抛出ClassCastException
- 引起:java.lang.ClassCastException:java.sql.Timestamp无法强制转换为java.sql.Date
- 如何在java中将ArrayList转换为String ,Arraylist包含VO对象
- Hibernate – > ArrayList不能转换为Set
- ClassCastException将List 转换为Class