具有方法重载

时间:2018-01-29 04:57:04

标签: java generics

我正在尝试构建一个类型为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我唯一的选择?

2 个答案:

答案 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;代理人会看。