我正在尝试构建一个类型为T extends BaseClient
的对象实例的工厂,返回的实例中有一个方法T
被重载以执行一些额外的逻辑。我无法修改T
类的代码(这就是我需要重载的原因),也不能修改基本抽象类BaseClient
的代码。
对于具体的,明确定义的类型,这很容易做到。例如,给定FooClient extends BaseClient
,我可以这样做:
public class FooClientFactory {
public static FooClient create() {
return new FooClient() {
@Override
public void theMethod() {
super.theMethod();
someAdditionalLogic();
}
};
}
}
但是在我需要处理潜在的许多不同类型的客户端(BaseClient
的所有子类)的世界中,我希望有这样的东西:
public class ClientFactory<T extends BaseClient> {
public static T create() {
return ...;
}
}
所以我不必为每个具体的子类复制上面的代码。
不幸的是,写new T() { ... }
是不可能的。它也不可能使用Class<T>
并在其上调用newInstance()
,因为这不允许我覆盖theMethod()
的行为。使用Proxy
实例将允许我拦截方法调用并匹配方法名称(丑陋)以在需要时执行someAdditionalLogic()
,但因为BaseClient
不是接口 - 我可以让它成为一个 - 我无法构建一个代理(更准确地说,在我的实际用例中,我确实有一个我可以使用的接口,特定于每个客户端,但是有些地方返回的实例需要被强制转换为BaseClient
,由于代理实例的工作原理,返回的Proxy
实例不会“技术上”实现/通告,导致ClassCastException
s。
那么,是什么给出的?这在Java中是完全可能的,还是每个FooClientFactory
类型的复制粘贴XXClient
我唯一的选择?
答案 0 :(得分:1)
这个解决方案可能不比评论中讨论的更理想。但是,如果组合是系统中可接受的解决方案,那么在使用检测之前,您可能会考虑这样的事情:
public class WrapperClient<T extends BaseClient> extends BaseClient {
T realClient;
public WrapperClient(T realClient) {
this.realClient = realClient;
}
public void theMethod() {
realClient.theMethod();
someAdditionalLogic();
}
}
然后对于工厂,有一个抽象层:
public abstract class WrapperClientFactory<T extends BaseClient> {
public final WrapperClient<T> create() {
T realClient = createRealClient();
return new WrapperClient<T>(realClient);
}
protected abstract T createRealClient();
}
然后工厂实现可能看起来像这样
public class FooClientFactory extends WrapperClientFactory<FooClient> {
protected FooClient createRealClient() {
return new FooClient();
}
}
然后消费者会这样做:
BaseClient client = FooClientFactory.create();
client.theMethod();
在这里,客户端实际上是WrapperClient的一个实例,而WrapperClient又会委托给FooClient,而不是FooClient本身,所以它有点像hacky。但它将允许您在抽象层的任意子类中扩展逻辑。
如果消费者直接引用他们实际使用的子类并不重要,我希望这种方法可以用于评论中讨论的一些内容。
如果这种方法基本上没问题,但是客户端子类需要能够提供与纯BaseClient不同的接口,那么您可以添加另一个层并使WrapperClient成为抽象。
这种方法的另一个缺点是你失去了工厂中的静态方法。如果这在您的系统中很重要,您可以创建一个工厂工厂来隐藏消费者的实例化。
答案 1 :(得分:1)
如果Ethan的回答不是你想要的,那么简短的回答就是#34;核心Java中没有任何东西可以让你做你想要做的事情&#34 ;
据说这并非不可能,但您需要使用字节码操作库。这就是Spring能够代理所有非接口bean的方式(如果你不知道,Spring中所有注入的bean实际上都是代理),它使用CGLIB(source)。基于this other Stackoverflow answer,也可以使用Javasist进行代理非接口类;后来似乎相当简单,接近正常&#34;代理人会看。