英文:
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
的类,它实现了Entity
。EntityImpl
有额外的方法,而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();
}
}
我尝试过的方法
- 如果我使用上面的代码,它将不起作用,因为
entity.do_something_else()
找不到,因为do_something_else
是EntityImpl
独有的方法,在Entity
接口中没有定义。
public class StrategyImpl Strategy{
void strategise(Entity entity){
entity.do_something_else();
}
}
- 我尝试在
StrategyImpl.strategise()
的参数定义中使用EntityImpl
对象而不是Entity
对象,但这不起作用,因为它会认为我没有实现Entity.strategise()
。
public class StrategyImpl Strategy{
void strategise(EntityImpl entity){
EntityImpl.do_something_else();
}
}
- 我尝试使用泛型和通配符,但我对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 aEntityImpl
object to be passed as the parameter to the function it implements fromStrategy
:strategise
.- This is because
StrategyImpl.strategise()
calls the method unique toEntityImpl
which is not defined inEntity
:do_something_else()
.
public class StrategyImpl implements Strategy {
void strategise(Entity entity){
entity.do_something_else();
}
}
What I have tried to do
- If I use the code above, it won't work as
entity.do_something_else()
can't be found asdo_something_else
is unique toEntityImpl
and not defined inEntity
.
public class StrategyImpl Strategy{
void strategise(Entity entity){
entity.do_something_else();
}
}
- I've tried using a
EntityImpl
object instead of anEntity
object as the parameter definition inStrategyImpl.strategise()
but this doesn't work as it thinks I'm not implementingEntity.strategise()
public class StrategyImpl Strategy{
void strategise(EntityImpl entity){
EntityImpl.do_something_else();
}
}
- 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>
类型的集合,其中X
是Entity
的一种类型。这个集合可以包含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<T extends Entity>{
void strategise(T entity);
}
class StrategyImpl implements Strategy<EntityImpl>{
public void strategise(EntityImpl entity){
entity.do_something_else();
}
}
This way, a Strategy<EntityImpl>
is a different type from a Strategy<SomeOtherEntity>
.
This restricts you from doing a bunch of things (that you should not do anyway), such as putting a bunch of general Strategy
s 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<? super X>
where X
is an type of Entity
. This collection can then contain Strategy<AnySuperClassOfX>
. 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
的东西。它开了一张兑现不了的支票。
你的其他问题都源于这个基本的设计缺陷。你可以绕过它(比如另一个答案,如果你传递一个非EntityImpl
的Entity
对象,它将悄悄地什么也不做,这似乎是一件非常愚蠢的事情。至少要抛出一些异常!)
但是,有一个解决方案。
一个简单的解决方案可能是:好吧,医生,当我按这里时很疼! - 那就别再按那里。如果你只是因为在某本书中读到或听别人说这是个好主意才创建接口,那么实际上不是。别这么做了。删除你的接口,将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<E extends Entity> {
public void strategise(E entity);
}
And now you can implement your StrategyImpl class:
public class StrategyImpl implements Strategy<EntityImpl> {
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论