在接口继承中使用Java泛型进行代码重用

在接口继承中使用Java泛型进行代码重用,java,generics,inheritance,design-patterns,Java,Generics,Inheritance,Design Patterns,我正在尝试使用Java中的面向对象技术为状态机创建一个框架。每个系统一次只能有一个状态处于活动状态 public interface State {} public interface System { State currentState(); } 假设我想添加一种新类型的系统,它应该定期更新以检查和更改其状态,称为UpdateableSystem。它只能具有类型为UpdateableState的状态 public interface UpdateableState extends

我正在尝试使用Java中的面向对象技术为状态机创建一个框架。每个
系统
一次只能有一个
状态
处于活动状态

public interface State {}
public interface System { 
    State currentState();
}
假设我想添加一种新类型的系统,它应该定期更新以检查和更改其状态,称为
UpdateableSystem
。它只能具有类型为
UpdateableState
的状态

public interface UpdateableState extends State {
    /**
     * Check to see if the system should switch to a different state.
     * @returns new state of the system
     */
    UpdateableState update();
}

public interface UpdateableSystem extends System {
    @Override
    UpdateableState currentState();
    /** 
     * Runs update method of the current state, or otherwise updates the system.
     * @returns the current state of the system after the update
     */
    UpdateableState update();
}
我重写了
currentState
方法,只返回
UpdateableState
,而不返回
State
,因为
UpdateableSystem
只能具有
UpdateableState
类型的状态,而使用
UpdateableSystem
的客户端需要
UpdateableState
的状态

为了避免在创建更多子类时多次重写许多方法,泛型似乎是解决方案。 以下是更新的界面

public interface System<SystemState extends State> {
    SystemState currentState();
}
突然,这些参数化类型变成了一大堆样板文件,令人困惑,难以维护

这将是一个具体实施的示例:

class CarContext implements Context {...}
interface CarState extends ContextState<CarContext, CarState> {...}
    class CarDrivingState implements CarState {}
    class CarIdleState implements CarState {}
class Car implements ContextSystem<CarContext, CarState> {...}
类CarContext实现上下文{…} 接口CarState扩展了ContextState{…} 类CarDrivingState实现CarState{} 类CarIdleState实现CarState{} 类Car实现ContextSystem{…} 每个具体的
CarState
将能够访问
CarContext
接口提供的附加方法,因为从
ContextState
继承的方法签名将由
CarContext
类型参数化。因此,
updateWithContext
的上下文参数不需要显式强制转换


最终,在添加额外的类型以进行参数化时,问题是太多的样板文件,我不知道有哪种设计方案可以通过继承实现代码重用,并且还可以维护一个强大的类型系统,尽可能避免显式转换。有什么改进此设计的建议吗?

花了一天的时间思考这个问题,并使用了一些示例代码,我认为您已经找到了解决此问题的最佳解决方案,尽管您可能会从正确遵守该规则中受益

通过在接口声明中使用泛型类型的全名,实际上会使它们更难阅读,尤其是在使用方法时。泛型类型很容易与实际的接口或类名混淆。更不用说它们占据了更多的空间。让我们看看您的代码在以下标准中的样子:

public interface State{}
public interface System<S extends State> {
    S currentState();
}

public interface UpdateableState<S extends UpdateableState<S>> extends State {
    S update();
}
public interface UpdateableSystem<S extends UpdateableState<S>> extends System<S> {
    S update();
}

public interface Context{}
public interface ContextState<C extends Context, S extends ContextState<C, S>> extends UpdateableState<S> {
    S updateWithContext(C context);
}
public interface ContextSystem<C extends Context, S extends ContextState<C, S>> extends UpdateableSystem<S> {
    // Some methods here that require SystemContext type
}

class CarContext implements Context {...}
interface CarState extends ContextState<CarContext, CarState> {...}
class CarDrivingState implements CarState {}
class CarIdleState implements CarState {}
class Car implements ContextSystem<CarContext, CarState> {...}
类型系统仍然足够强大,可以防止我们爆炸

GenericSystem<BadExampleContextState> s = new GenericSystem<>(new BadExampleContextState()); 
// error: type argument Generics.BadExampleContextState is not within bounds of type-variable S
GenericSystem s=新的GenericSystem(新的BadExampleContextState());
//错误:类型参数泛型。BadExampleContextState不在类型变量S的范围内

TL;DR:我认为您可以放松泛型约束,提供一个不太详细的接口声明,而不承担运行时失败的风险。

Java泛型不是模板类型。这个
接口CarState扩展ContextState
(例如),也可以写入
接口CarState扩展ContextState
-类型的本地名称只是一个标签。我不确定你的意思,它们在
ContextState
接口的定义中充当标签,但是在
CarState
接口的定义中,它们必须是实际类型。据我所知,实际的接口或类。我会再试一次;Java泛型是一种编译时类型检查系统。它们是在编译(因此没有运行时开销)到
java.lang.Object
之后生成的;您所期待的是它们的行为类似C++模板类型。它们没有。@Elliott如果我误解了,很抱歉,但我可以将其扩展为原始类型:
interface CarState extends ContextState
,由于擦除的原因,它的功能可能相同。但是,我会收到编译器警告,并且在编译时它会缺少相同的类型检查保证。我不确定擦除在这种情况下对我有什么帮助。擦除对你没有帮助。擦除是泛型如何在Java中实现的直接(且不可避免)结果。
public interface State{}
public interface System<S extends State> {
    S currentState();
}

public interface UpdateableState<S extends UpdateableState<S>> extends State {
    S update();
}
public interface UpdateableSystem<S extends UpdateableState<S>> extends System<S> {
    S update();
}

public interface Context{}
public interface ContextState<C extends Context, S extends ContextState<C, S>> extends UpdateableState<S> {
    S updateWithContext(C context);
}
public interface ContextSystem<C extends Context, S extends ContextState<C, S>> extends UpdateableSystem<S> {
    // Some methods here that require SystemContext type
}

class CarContext implements Context {...}
interface CarState extends ContextState<CarContext, CarState> {...}
class CarDrivingState implements CarState {}
class CarIdleState implements CarState {}
class Car implements ContextSystem<CarContext, CarState> {...}
public interface State{}
public interface System<S> {
    S currentState();
}

public interface UpdateableState<S> extends State {
    S update();
}
public interface UpdateableSystem<S> extends System<S> {
    S update();
}

public interface Context{}
public interface ContextState<C, S> extends UpdateableState<S> {
    S updateWithContext(C context);
}
public interface ContextSystem<C, S> extends UpdateableSystem<S> {
    S updateWithContext(C context);
}

public class ExampleContext implements Context{}
public class ExampleContextState implements ContextState<ExampleContext, ExampleContextState>{
    @Override
    public ExampleContextState updateWithContext(ExampleContext context)
    {
        return this;
    }

    @Override
    public ExampleContextState update()
    {
        return this;
    }
}
public class ExampleContextSystem implements ContextSystem<ExampleContext, ExampleContextState>{
    private ExampleContextState currentState;
    public ExampleContextSystem(){
        currentState = new ExampleContextState();
    }
    @Override
    public ExampleContextState updateWithContext(ExampleContext context)
    {
        this.currentState = currentState.updateWithContext(context);
        return currentState;
    }

    @Override
    public ExampleContextState update()
    {
        this.currentState = currentState.update();
        return currentState;
    }

    @Override
    public ExampleContextState currentState()
    {
        return currentState;
    }

}
public class BadExampleContextState implements ContextState<Integer, String>{
    @Override
    public String updateWithContext(Integer context)
    {
        return "Garbage";
    }

    @Override
    public String update()
    {
        return "Garbage";
    }
}
public class GenericSystem<S extends UpdateableState<S>> implements UpdateableSystem<S>{
    private S currentState;
    public GenericSystem(S startingState){
        this.currentState = startingState;
    }
    @Override
    public S update()
    {
        this.currentState = currentState.update();
        return currentState;
    }

    @Override
    public S currentState()
    {
        return currentState;
    }

}
GenericSystem<BadExampleContextState> s = new GenericSystem<>(new BadExampleContextState()); 
// error: type argument Generics.BadExampleContextState is not within bounds of type-variable S