Java 将方法参数定义为子类型

Java define method parameter as a subtype

我想做什么

我有一个名为 strategy 的接口,它有一个方法 strategise。此方法将名为 Entity 的接口作为参数。

public interface Strategy{

  void strategise(Entity entity);

}
public interface Entity {
   void do_something();
}

我有一个名为 EntityImpl 的 class 实现了 EntityEntityImpl 有一个额外的方法,Entity 没有。

public class EntityImpl implements Entity{
    
    void do_something()         // defined in Entity interface
    void do_something_else()    // unique to Person

}

我有另一个 class StrategyImpl 实现了 strategy.

public class StrategyImpl implements Strategy {

  void strategise(Entity entity){
       entity.do_something_else();
  }
}

我尝试过的事情

  1. 如果我使用上面的代码,它将无法工作,因为无法找到 entity.do_something_else(),因为 do_something_elseEntityImpl 独有的,并且未在 [=20= 中定义].
public class StrategyImpl Strategy{

  void strategise(Entity entity){
       entity.do_something_else();
  }
}
  1. 我试过使用 EntityImpl 对象而不是 Entity 对象作为 StrategyImpl.strategise() 中的参数定义,但这不起作用,因为它认为我没有实现Entity.strategise()
public class StrategyImpl Strategy{

  void strategise(EntityImpl entity){
       EntityImpl.do_something_else();
  }
}
  1. 我试过使用泛型和通配符,但我不熟悉java。
public interface Strategy {

   void strategise((E implements Entity) entity);

}

任何帮助将不胜感激

感谢您的宝贵时间!

你的设计有点缺陷,因为如果我这样做会怎样?

Strategy s = new StrategyImpl();
s.strategise(new SomeOtherEntity());

这应该编译,因为 s.strategise 接受一个 EntitySomeOtherEntity 一个 Entity。但最终 StrategyImpl.strategise 将成为 运行。它只能处理 EntityImpl 个对象,不能处理 SomeOtherEntity!

您可以通过向 Strategy 添加泛型参数来解决此问题:

interface Strategy<T extends Entity>{

    void strategise(T entity);

}

class StrategyImpl implements Strategy<EntityImpl>{

    public void strategise(EntityImpl entity){
        entity.do_something_else();
    }
} 

这样,Strategy<EntityImpl>Strategy<SomeOtherEntity> 不同的类型

这限制了你做一堆事情(无论如何你都不应该做),比如把一堆通用的 Strategy 放在一个集合中。当你从那个集合中取出一个 Strategy 时,谁知道那个特定的 Strategy 接受什么样的 Entity

但是,您可以创建 Strategy<? super X> 的集合,其中 XEntity 的一种类型。该集合然后可以包含 Strategy<AnySuperClassOfX>。这是有效的,因为 pf PECS.

StrategyImpl expects a EntityImpl object to be passed as the parameter to the function it implements from Strategy : strategise.

这是个大问题。

您的 StrategyImpl class 实现了 Strategy,这意味着 StrategyImpl 实现了规定的契约,其中包括它具有 void strategise(Entity) 方法实现的概念.

但是,你没有那个。你所拥有的只是 void strategise(EntityImpl) 的一个实现,如果你得到一个不是 EntityImpl 的实体,你所能做的就是放弃,然后崩溃。这使您的 StrategyImpl 成为不应该 implements Strategy 的东西。它正在写一张无法兑现的支票。

你的其他问题源于这个基本的设计缺陷。你可以解决它(比如另一个答案,如果你传递一个非 EntityImpl Entity 对象,它会默默地不做任何事情,这似乎是一个非常愚蠢的事情。至少扔一些东西!

但是,有一个解决方案。

一个简单的解决办法可能是:好吧,医生,我按这里很疼! - 所以不要再按那里了。如果您只是因为阅读某本书或从某人那里听说这是一个好主意而制作界面,那么它不是。停止这样做。删除你的界面,将你的 StrategyImpl class 重命名为 Strategy 并将你的 EntityImpl class 重命名为 Entity 然后继续。你可能会想:“但是,不行!现在我以后不能选择其他实现了!” - 但我们已经知道这是一个错误的选择:你已经不能。你的 StrategyImpl class 将失败(默默地!那些是非常糟糕的错误,因为它们很难找到!)如果你曾经乱搞任何其他实现如果 Entity 除了 EntityImpl,所以你的复杂接口+classes mess 也无法处理它。

如果你真的对这个界面有意见+class你弄得一团糟,还是有解决办法的。您需要使类型声明的 implements Strategy 部分更具体。你想说:我实现了一种特定的策略:只能处理EntityImpl个对象的那种。您也可以使用泛型来做到这一点:

public interface Strategy<E extends Entity> {
    public void strategise(E entity);
}

现在您可以实施 StrategyImpl class:

public class StrategyImpl implements Strategy<EntityImpl> {
    public void strategise(EntityImpl entity) {
        // feel free to call .doSomethingElse() here
    }
}

现在实际上不可能让某些具有 Strategy 变量的代码仅对其调用 strategise(x),其中 x 是任何随机实体。没有;泛型必须匹配。这正是您想要的:编译器会为您检查。