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

huangapple go评论83阅读模式
英文:

Java define method parameter as a subtype

问题

我想要做的事情

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

public interface Strategy {

  void strategise(Entity entity);

}
public interface Entity {
   void do_something();
}

<br>

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

public class EntityImpl implements Entity {
    
    void do_something()         // 在Entity接口中定义
    void do_something_else()    // EntityImpl独有的方法

}

我还有另一个类StrategyImpl,它实现了strategy接口。

  • StrategyImpl期望一个EntityImpl对象被传递给它从Strategy接口实现的函数的参数。
  • 这是因为StrategyImpl.strategise()调用了EntityImpl独有的方法,而这个方法在Entity接口中没有定义:do_something_else()
public class StrategyImpl implements Strategy {

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

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

我尝试过的方法

  1. 如果我使用上面的代码,它将不起作用,因为entity.do_something_else()找不到,因为do_something_elseEntityImpl独有的方法,在Entity接口中没有定义。
public class StrategyImpl Strategy{

  void strategise(Entity entity){
       entity.do_something_else();
  }
}
  1. 我尝试在StrategyImpl.strategise()的参数定义中使用EntityImpl对象而不是Entity对象,但这不起作用,因为它会认为我没有实现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);

}

任何帮助将不胜感激

感谢您的时间!

英文:

What I want to do

I have an interface called strategy that has one method strategise. This method takes an interface called Entity as a parameter.

public interface Strategy{

  void strategise(Entity entity);

}
public interface Entity {
   void do_something();
}

<br>

I have a class called EntityImpl that implements Entity. EntityImpl has an additional methods that Entity does not.

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

}

I have another class StrategyImpl that implements strategy.

  • StrategyImpl expects a EntityImpl object to be passed as the parameter to the function it implements from Strategy : strategise.
  • This is because StrategyImpl.strategise() calls the method unique to EntityImpl which is not defined in Entity : do_something_else().
public class StrategyImpl implements Strategy {

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

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

What I have tried to do

  1. If I use the code above, it won't work as entity.do_something_else() can't be found as do_something_else is unique to EntityImpl and not defined in Entity.
public class StrategyImpl Strategy{

  void strategise(Entity entity){
       entity.do_something_else();
  }
}
  1. I've tried using a EntityImpl object instead of an Entity object as the parameter definition in StrategyImpl.strategise() but this doesn't work as it thinks I'm not implementing Entity.strategise()
public class StrategyImpl Strategy{

  void strategise(EntityImpl entity){
       EntityImpl.do_something_else();
  }
}
  1. I've tried using generics and wildcards, but I'm not familiar with java.
public interface Strategy {

   void strategise((E implements Entity) entity);

}

Any help would be very appreciated

Thanks for your time!

答案1

得分: 5

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

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

这应该能够编译通过,因为s.strategise接受一个Entity,而SomeOtherEntity实际上是一个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>。这样做是有效的,因为涉及到PECS原则

英文:

Your design is a little flawed, because what if I did this?

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

This should compile, since s.strategise accepts an Entity and SomeOtherEntity is an Entity. But eventually it's StrategyImpl.strategise that's going to be run. It can only handle EntityImpl objects, not SomeOtherEntity!

You can solve this problem by adding a generic parameter to Strategy:

interface Strategy&lt;T extends Entity&gt;{

    void strategise(T entity);

}

class StrategyImpl implements Strategy&lt;EntityImpl&gt;{

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

This way, a Strategy&lt;EntityImpl&gt; is a different type from a Strategy&lt;SomeOtherEntity&gt;.

This restricts you from doing a bunch of things (that you should not do anyway), such as putting a bunch of general Strategys in a collection. Who knows what kind of Entity that particular Strategy accepts when you take a Strategy out of that collection?

You can, however, create a collection of Strategy&lt;? super X&gt; where X is an type of Entity. This collection can then contain Strategy&lt;AnySuperClassOfX&gt;. This works because pf PECS.

答案2

得分: 3

> StrategyImpl希望作为参数传递一个EntityImpl对象到它从Strategy : strategise实现的函数中。

这是一个很大的问题。

你的StrategyImpl类实现了Strategy,这意味着StrategyImpl满足了所述的契约,其中包括它对void strategise(Entity)方法的实现。

但是,你没有那个方法。你只有一个void strategise(EntityImpl)的实现,如果你传递一个不是EntityImpl的Entity,你能做的就只有放弃并崩溃。这使得你的StrategyImpl变成了一个不应该implements Strategy的东西。它开了一张兑现不了的支票。

你的其他问题都源于这个基本的设计缺陷。你可以绕过它(比如另一个答案,如果你传递一个非EntityImplEntity对象,它将悄悄地什么也不做,这似乎是一件非常愚蠢的事情。至少要抛出一些异常!)

但是,有一个解决方案。

一个简单的解决方案可能是:好吧,医生,当我按这里时很疼! - 那就别再按那里。如果你只是因为在某本书中读到或听别人说这是个好主意才创建接口,那么实际上不是。别这么做了。删除你的接口,将StrategyImpl类重命名为Strategy,将EntityImpl类重命名为Entity,然后继续进行。你可能会想:“但是,不!现在我不能以后选择其他实现了!” - 但我们已经可以断定这是一个错误的选择:你已经不能这样做了。如果你对Entity使用除了EntityImpl之外的任何其他实现,你的StrategyImpl类将失败(悄悄地!这些错误非常严重,因为它们很难找到!),所以你混乱的接口+类的方式也无法处理这个问题。

如果你真的有理由创建这个接口+类的混乱,仍然有一个解决方案。你的类型声明中的implements Strategy部分需要更具体。你想要表达的是:我实现了一种特定类型的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) {
        // 在这里随意调用 .doSomethingElse()
    }
}

实际上,现在某些具有Strategy变量的代码不可能只是在它上面调用strategise(x),其中x是任意的Entity。不行;泛型必须匹配。这正是你想要的:编译器会为你检查。

英文:

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

That's a big problem.

Your StrategyImpl class implements Strategy, and that implies that StrategyImpl fulfills the stated contract, which includes the notion that it has an implementation for the void strategise(Entity) method.

But, you don't have that. All you have is an implementation for void strategise(EntityImpl), and if you're fed an Entity that isn't an EntityImpl, all you can do is give up, and crash. That makes your StrategyImpl a thing that shouldn't be implements Strategy. It's writing a cheque it can't cash.

The rest of your problems stem from this fundamental design wart. You can work around it (such as the other answer, which will silently do nothing if you pass a non-EntityImpl Entity object, which seems like an incredibly stupid thing to. At the very least throw something!

But, there's a solution.

One simple solution could be: Well, doctor, it hurts when I press here! - So stop pressing there. If you're just making interfaces because you read in some book or heard from somebody that this is a good idea, well, it isn't. Stop doing it. delete your interfaces, rename your StrategyImpl class to Strategy and your EntityImpl class to Entity and just move on. You may think: "But, no! Now I can't choose other implementations later on!" - but we can already tell that is a false choice: You already can't. Your StrategyImpl class will fail (silently! Those are really bad bugs as they are hard to find!) if you ever mess around with having any other implementation if Entity other than EntityImpl, so your convoluted interfaces+classes mess can't deal with it either.

If you really do have a point to this interface+class mess you've made, there is still a solution. You need for the implements Strategy part of your type declaration to be more specific. You want to say: I implement a specific sort of Strategy: The kind that can only deal with EntityImpl objects. You can do that, too, with generics:

public interface Strategy&lt;E extends Entity&gt; {
    public void strategise(E entity);
}

And now you can implement your StrategyImpl class:

public class StrategyImpl implements Strategy&lt;EntityImpl&gt; {
    public void strategise(EntityImpl entity) {
        // feel free to call .doSomethingElse() here
    }
}

It is now in fact impossible for some code that has a Strategy variable to just call strategise(x) on it, where x is any random Entity. Nope; the generics have to match up. Which is exactly what you want: The compiler will check for you.

huangapple
  • 本文由 发表于 2020年10月11日 10:25:06
  • 转载请务必保留本文链接:https://go.coder-hub.com/64300080.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定