在同类中实现同一类的基类中使用泛型的好处

时间:2017-01-01 21:38:53

标签: java generics

我最近在代码中遇到了这个我没写过的场景,虽然这种方法可能有一些设计好处,但我似乎无法从我自己的大脑中挤出这个理由。所以在我走之前看起来很愚蠢之前,我希望能在这里得到一些反馈。

服务界面如下:

public interface Service {...}

然后,一个基类,它向服务接口添加一个通用引用,其中T扩展了Service,但是整个基类也实现了接口。像这样:

public class ServiceBase<T extends Service> implements Service {...}

你为什么要这样做?我注意到在实践中,ServiceBase的扩展始终使用与正在声明的类相同的类名;所以这里没有任何神奇的多态效益。像这样:

public class MyService extends ServiceBase<MyService> {...}

并且,MyService类永远不是通用的容器(例如,我不相信这是在发送某种自包含列表,其中MyService可以包含MyServices列表)。

有关为何会这样做的任何想法/想法?

3 个答案:

答案 0 :(得分:4)

  

你为什么要这样做?我注意到在实践中延伸了   ServiceBase始终使用与T相同的类名   被宣布;因此,并没有任何神奇的多态效益   这里。

泛型不存在以创造神奇的多态性。它主要是一种在编译时为类型添加约束的方法,以便在运行时减少笨拙的强制类型和错误类型。

在您的情况下,假设ServiceBase类是抽象的并且具有process()方法,该方法需要在每次调用时创建我们在参数化类型中声明的具体类的新实例。
我们称之为抽象方法createService()

不使用泛型,我们可以声明方法public abstract ServiceBase createService()

没有泛型的ServiceBase

public abstract class ServiceBase implements Service {

    public abstract ServiceBase createService();

    @Override
    public void process() {
         createService().process();
    }

}

使用此声明,具体类可以返回ServiceBase的任何实例。

例如,以下子类将被编译,因为我们不会强制将返回的createService()类型更改为当前声明的类型。

没有泛型的MyService

public class MyService extends ServiceBase {

    @Override
    public ServiceBase createService() {    
        return new AnotherService();
    }

}

但是如果我在基类中使用泛型:

带泛型的ServiceBase

public abstract class ServiceBase<T extends Service> implements Service {

    public abstract T createService();

    @Override
    public void process() {
         createService().process();
    }

}

具体类没有选择,它被强制更改返回的createService()类型,并声明参数化类型。

使用泛型的MyService

public class MyService extends ServiceBase<MyService> {

    @Override
    public MyService createService() {  
        return new MyService();
    }

}

答案 1 :(得分:2)

我使用你的类和接口声明构建了一个例子(除了我使ServiceBase抽象),这应该说明泛型类型的使用:

public interface Service {
    int configure(String cmd);
}

public abstract class ServiceBase<T extends Service> implements Service {
    private ServiceManager manager;     

    public ServiceBase(ServiceManager manager){
        this.manager = manager;
    }

    public final void synchronize(T otherService){
        manager.configureEnvironment(otherService.configure("syncDest"), configure("syncSrc"));
        synchronizeTo(otherService);
    }

    protected abstract void synchronizeTo(T otherService);
}

public class ProducerService extends ServiceBase<ConsumerService> {

    public ProducerService(ServiceManager manager) {
        super(manager);
    }

    @Override
    protected void synchronizeTo(ConsumerService otherService) { 
        /* specific code for synchronization with consumer service*/ 
    }           

    @Override
    public int configure(String cmd) { ... }
}

public class ConsumerService extends ServiceBase<ProducerService> {

    public ConsumerService(ServiceManager manager) {
        super(manager);
    }

    @Override
    protected void synchronizeTo(ProducerService otherService) {
        /* specific code for synchronization with producer service */           
    }

    @Override
    public int configure(String cmd) { ... }

}

想象一下,我们拥有由ServiceManager管理的服务,它可以配置服务的环境,以便它们可以相互同步。如何解释configure命令取决于特定服务。因此,configure()声明驻留在我们的界面中。

ServiceBase处理通常在两个服务想要同步时必须发生的基本同步事件。 ServiceBase的各个实现不应该处理这个问题。

但是ServiceBase不知道自身的特定实现如何与特定的其他服务实现同步。因此,它必须将此部分同步委托给其子类。

现在仿制药进入了剧中。 ServiceBase也不知道它能够与哪种类型的服务同步。他还要将此决定委托给其子类。他可以使用构造T extends Service

来做到这一点

鉴于此结构现在想象ServiceBase的两个具体子类:ProducerServiceConsumerService;消费者服务只能与生产者服务同步,反之亦然。因此,这两个类在其声明ServiceBase<ConsumerService>中分别指定ServiceBase<ProducerService>

<强>结论

就像超类可以使用抽象方法将功能的实现委托给它们的子类一样,超类可以使用泛型类型参数将类型占位符的“实现”委托给它们的子类。

答案 2 :(得分:0)

您尚未发布使用类型参数的这些类的任何定义(这很可能会传达此设计背后的基本原理,或者可能缺少它......),但在所有情况下, type参数是一种参数化类的方法,就像可以参数化的方法一样。

ServiceBase类实现Service。这告诉我们它实现了Service的契约(方法)(更准确地说,它的子类可以作为实现)。

同时,ServiceBase采用的类型参数是Service的子类型。这告诉我们服务实现可能与另一种实现类型(可能与当前类型相同)具有“关系”。这种关系可以是特定设计要求所需的任何东西,例如,此实现可以委派的Service类型,可以调用此服务的Service类型等。

我阅读以下声明的方式

public class ServiceBase<T extends Service> implements Service {...}

粗略地说: ServiceBase是服务的基本实现,可以与其他类型的服务建立静态类型关系

这两个方面是完全独立的。