将最终类强制转换为该类未声明要实现的兼容接口

时间:2016-03-18 22:01:17

标签: java interface casting duck-typing

有时在Java中,有一种情况是您使用提供final class Car的库,并希望它实现一些Vehicle接口,以便您可以Truck和{{1在您的代码中将类全部视为Vehicle。但是Car是最终的,并没有实现任何接口。

如何将其他人的最终Bus课程投放到我的Car界面,以便我可以像其他车辆一样传递它?在实例方法名称,参数类型和返回类型方面,Vehicle上的每个实例方法都与Car上的类似方法100%兼容。从Duck-Typing的角度来看,它将是相同的。

我知道我可以创建一个Vehicle包装类,它只是将每个方法调用委托给一个内部Car对象。这将是The Java Way。但我只是想知道是否有一种技术可以将一个类实际转换为不相关(但100%兼容)的接口。如果答案是邪恶的,那就好了。

4 个答案:

答案 0 :(得分:5)

使用代理执行此操作:

import java.lang.reflect.Proxy;

public class DuckTyping {

    static final class Car{
        public void honk(){
            System.out.println("honk");
        }
    }

    interface Vehicle{
        void honk();
    }

    public static void main(String[] a){
        Car c = new Car();
        Vehicle v = (Vehicle) Proxy.newProxyInstance(Vehicle.class.getClassLoader(), new Class[]{Vehicle.class}, (proxy, method, args) -> 
            Car.class.getMethod(method.getName(), method.getParameterTypes()).invoke(c, args)
        );

        v.honk();
    }
}

通用方法:

    static <T> T proxyCast(Object targetObject, Class<T> targetClass) {
        return (T) Proxy.newProxyInstance(targetClass.getClassLoader(), new Class[]{targetClass}, (proxy, method, args) -> 
            targetObject.getClass().getMethod(method.getName(), method.getParameterTypes()).invoke(targetObject, args)
        );
    }

答案 1 :(得分:3)

  1. 类中的最终修饰符不允许更改其状态,因此无法修改班级。

  2. 如果您从父类或工具扩展到界面,则 仅适用,没有其他办法(但您不能因为上课是最后的)

  3. 在这种情况下,您有三个选择:        1.包装(你提到)。        2.反射(您可以在运行时中找出有关类及其方法的有用信息)。        3.创建自己的课程。

答案 2 :(得分:2)

最简单的方法是使用JDK Proxies:

public class DuckTyper {

    public static <T> T quack(Object target, Class<T> duck) {

        return (T) Proxy.newProxyInstance(duck.getClassLoader(), new Class[] { duck }, new DuckCostume(target));

    }

    private static class DuckCostume implements InvocationHandler {

         private final Object target;

         public DuckCostume(Object target) {

             this.target = target;

         }

         @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);
         }

    }

}

(有一堆未处理的异常,并且没有任何反射被缓存,所以在你这样做之前它会很慢,但这应该给你一个起点)

在此之后,你可以像这样对你的车进行打包:

Vehicle carVehicle = DuckTyper.quack(car, Vehicle.class);

答案 3 :(得分:-1)

正如你所提到的,我个人会为它创建一个包装类 也许对Car界面的引用会有所帮助吗?

public abstract class MyCar implements Vehicle {

    private Car car;

    (...)
}

没有太多方法可以做到这一点,我不确定我的方式是你想要的,也不确定它是完全有效的。