有时在Java中,有一种情况是您使用提供final class Car
的库,并希望它实现一些Vehicle
接口,以便您可以Truck
和{{1在您的代码中将类全部视为Vehicle。但是Car是最终的,并没有实现任何接口。
如何将其他人的最终Bus
课程投放到我的Car
界面,以便我可以像其他车辆一样传递它?在实例方法名称,参数类型和返回类型方面,Vehicle上的每个实例方法都与Car上的类似方法100%兼容。从Duck-Typing的角度来看,它将是相同的。
我知道我可以创建一个Vehicle
包装类,它只是将每个方法调用委托给一个内部Car对象。这将是The Java Way。但我只是想知道是否有一种技术可以将一个类实际转换为不相关(但100%兼容)的接口。如果答案是邪恶的,那就好了。
答案 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.创建自己的课程。
答案 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;
(...)
}
没有太多方法可以做到这一点,我不确定我的方式是你想要的,也不确定它是完全有效的。