【问题标题】:Java define method parameter as a subtypeJava将方法参数定义为子类型
【发布时间】:2020-10-11 02:25:06
【问题描述】:

我想做什么

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

public interface Strategy{

  void strategise(Entity entity);

}
public interface Entity {
   void do_something();
}

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

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

}

我有另一个类StrategyImpl 实现了strategy

  • StrategyImpl 期望将 EntityImpl 对象作为参数传递给它从 Strategy 实现的函数:strategise
  • 这是因为StrategyImpl.strategise() 调用了EntityImpl 独有的方法,该方法未在Entity 中定义:do_something_else()
public class StrategyImpl implements Strategy {

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

我尝试过的事情

  1. 如果我使用上面的代码,它将无法工作,因为无法找到 entity.do_something_else(),因为 do_something_elseEntityImpl 独有的,并且未在 Entity 中定义。
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);

}

任何帮助将不胜感激

感谢您的宝贵时间!

【问题讨论】:

  • 虽然你没有这么说,但我猜你是想把所有的策略都放在一个集合里什么的?
  • Nup。只是每个实体对象都有唯一的策略对象。但是策略有很多通用的方法。
  • 这听起来就像泛型的情况一样:<E extends Entity>

标签: java generics inheritance wildcard implementation


【解决方案1】:

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

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&lt;EntityImpl&gt;Strategy&lt;SomeOtherEntity&gt;不同的类型

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

但是,您可以创建Strategy&lt;? super X&gt; 的集合,其中XEntity 的一种类型。然后,此集合可以包含 Strategy&lt;AnySuperClassOfX&gt;。这是因为 pf PECS.

【讨论】:

  • 非常感谢!如果不是太麻烦,我想知道你是否可以告诉我这个设计是否是一个好主意,因为只有 1 个实例(在 6 个中)具有该额外功能的策略。我该怎么办?
【解决方案2】:

StrategyImpl 期望将 EntityImpl 对象作为参数传递给它从 Strategy 实现的函数:strategise。

这是个大问题。

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

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

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

但是,有一个解决方案。

一个简单的解决方案可能是:好吧,医生,我按这里的时候很痛! - 所以别再按那里了。如果你只是因为读过一些书或从某人那里听说这是个好主意而制作界面,那么,它不是。别这样了。删除您的接口,将您的StrategyImpl 类重命名为Strategy,将您的EntityImpl 类重命名为Entity,然后继续。你可能会想:“但是,不!现在我以后不能选择其他实现了!” - 但我们已经可以看出这是一个错误的选择:你已经不能了。你的StrategyImpl 类将会失败(默默地!这些是非常糟糕的错误,因为它们很难找到!)如果你曾经在Entity 以外的EntityImpl 以外的任何其他实现上搞砸了,所以你的复杂接口+类混乱也应付不来。

如果你确实对你造成的这个接口+类的混乱有意见,那么仍然有一个解决方案。您需要更具体的类型声明的 implements Strategy 部分。你想说:我实现了一种特定的策略:只能处理EntityImpl 对象的那种。你也可以使用泛型来做到这一点:

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

现在你可以实现你的 StrategyImpl 类了:

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

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

【讨论】:

  • 非常感谢!如果不是太麻烦的话,我想知道你是否可以告诉我这个设计是否是一个好主意,因为只有 1 个实例(在 6 个中)具有该额外功能的策略。我该怎么办?
  • 很难说不知道这些东西代表什么,为什么会有“StrategyImpl”和“EntityImpl”(Impl 是一种代码味道),以及 Entity 和/或的另一种实现方式策略可能看起来像。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多