【问题标题】:Java dynamic adapterJava 动态适配器
【发布时间】:2011-06-24 03:30:53
【问题描述】:

我正在使用一些生成以下接口的 Java 代码。此代码不可修改。

interface A1 {
    public void run();
}

interface A2 {
    public void run();
}

...

interface A24 {
    public void run();
}

以下代码出现类转换错误。如何为我的界面动态构建适配器?

interface ARunnable {
    public void run();
}

public void someMethod() {
    // getARunnables() returns a list of A1, A2, ... A24
    List<ARunnable> runnables = (List<ARunnable>)getARunnables(); 

    for (ARunnable a : runnables) {
        a.run();
    }
}

【问题讨论】:

  • 您遇到 ClassCastException 是因为接口和类位于不同的类加载器中,对吧?
  • 这些接口A1->An共享有没有共同的接口?类似于:接口 A1 扩展 ARunnable { }

标签: java adapter


【解决方案1】:

由于无法修改接口以扩展 java.lang.Runnable,因此可以选择使用 java.lang.reflect.Proxy 来构建 Runnable 实例,委托给您的 A1、A2... 接口。

这不是微不足道的,但是看看这个使用 java.lang.reflect.Proxy 的例子。该示例只是根据方法名称委托给一个委托对象。

public class ProxyTest
{
    public static void main(String... args)
    {
        List<?> instances = Arrays.asList(new A1());
        List<ARunnable> runnableInstances = new ArrayList<ARunnable>(instances.size());
        for (Object instance : instances)
        {
            ARunnable runnableInstance = (ARunnable)Proxy.newProxyInstance(
                                            ARunnable.class.getClassLoader(), 
                                            new Class<?>[] {ARunnable.class}, 
                                            new RunnableWrapper(instance));
            runnableInstances.add(runnableInstance);
        }

        //Now we have a list of ARunnables!
        //Use them for something
        for (ARunnable runnableInstance : runnableInstances)
        {
            runnableInstance.run();
        }
    }

    private static class RunnableWrapper implements InvocationHandler
    {
        private final Object instance;

        public RunnableWrapper(Object instance)
        {
            this.instance = instance;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args)
                throws Throwable
        {
            //Ensure that your methods match exactly or you'll get NoSuchMethodExceptions here
            Method delegateMethod = instance.getClass().getMethod(method.getName(), method.getParameterTypes());
            return(delegateMethod.invoke(instance, args));
        }
    }

    public static class A1
    {
        public void run()
        {
            System.out.println("Something");
        }
    }

    public static interface ARunnable
    {
        public void run();
    }
}

另外我会建议你修复这条线

List<ARunnable> runnables = (List<ARunnable>)getARunnables(); 

你不应该忽视的那种类型的安全警告。该对象列表实际上并不包含 ARunnables,因此是 ClassCastException。

【讨论】:

    【解决方案2】:

    考虑这个示例代码(为了简单起见,没有循环):

        import java.lang.reflect.InvocationHandler;
        import java.lang.reflect.Method;
        import java.lang.reflect.Proxy;
    
        public class Main {
    
          interface Interface {
            public void run();
          }
    
          static class Hello /* does't implement Interface */{
    
            public void run() {
              System.out.println("Hello, world!!");
            }
          }
    
          static <T> T dirtyCast(Class<T> intrface, final Object target) {
    
            return intrface.cast(Proxy.newProxyInstance(
              intrface.getClassLoader(),
              new Class<?>[] { intrface }, new InvocationHandler() {
    
              @Override
              public Object invoke(Object proxy, Method method,
                Object[] args) throws Throwable {
    
                  Method targetMethod = target.getClass().getMethod(
                    method.getName(), method.getParameterTypes());
    
                  return targetMethod.invoke(target, args);
              }
    
            }));
          }
    
          public static void main(String[] args) {
    
            Interface proxy = dirtyCast(Interface.class, new Hello());
    
            proxy.run();
    
          }
        }
    

    如果您想传递参数或返回值或抛出异常,请不要认为此解决方案可行。问题是共享对象(作为参数、返回值和异常)需要存在于同一个(公共)类加载器中。这也意味着通常的 java lang 类型和异常都可以。

    您还必须牢记安全注意事项。类加载器可能有不同的(不兼容的)安全约束。

    如果您很快遇到麻烦,我会尝试为此设计的项目,例如 transloader

    玩得开心。

    【讨论】:

      【解决方案3】:

      在你的情况下,你可以简单地使用

      public void someMethod() throws ReflectiveOperationException {
          // getARunnables() returns a list of A1, A2, ... A24
          List runnables = (List)getARunnables(); 
      
          for (Object r : runnables) {
              r.getClass().getMethod("run").invoke(r);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 2011-12-26
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-01-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多