如何使用通配符迭代这个通用列表?
我有一个扩展另一个类的对象列表:
List<? extends Fruit> arguments;
现在,我想在这些对象上调用一个方法。调用类对于扩展 Fruit
的每个类都有一个方法 wash
,但不适用于 Fruit
抽象类:
void wash( Apple a);
void wash( Peach p);
我怎样才能应用方法清洗arguments
中的所有元素?这不起作用,因为我的 Wash 方法不接受 Fruit
参数:
for( Fruit f: arguments)
this.wash( f); // the wash() method is not a member of Fruit
有没有办法解决这个问题,而不必创建一个伞式方法 wash(Fruit)
?因为有数十种 wash( ? extends Fruit)
方法
...。
编辑:我所说的“调用类”是访问者。我无法更改任何 Fruit
类/子类。我只能对访客进行编程。这意味着无法将 wash()
方法(或任何其他方法)添加到抽象类 Fruit
中。
I have a List of objects that extend another class:
List<? extends Fruit> arguments;
Now, I want to invoke a method on these objects. The invoking class has a method wash
for each of the classes that extend Fruit
, but NOT for the Fruit
abstract class:
void wash( Apple a);
void wash( Peach p);
How can I apply the method wash to all the elements in arguments
? This DOESN'T work, as my wash methods don't accept Fruit
arguments:
for( Fruit f: arguments)
this.wash( f); // the wash() method is not a member of Fruit
Is there any way to solve this without having to make an umbrella method wash( Fruit)
? Because there are dozens of wash( ? extends Fruit)
methods...
.
EDIT: The "invoking class" I am talking about is a visitor. And I can't alter any of the Fruit
classes/subclasses. I can only program the visitor. This means it isn't possible to add the wash()
method (or any other methods, for that matter) to the abstract class Fruit
.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
欢迎来到双重动态调度的世界。
AFAIK,你不能在 Java 上轻松做到这一点。您可以通过两种方式完成此操作:quick'n'dirty 和 Visitor 方式:
Quick'n'dirty
您需要询问对象的类型,因此您需要 Fruit 上的 Wash 方法,它将调用重定向到根据其类型选择正确的函数:
quick'n'dirty 的问题是编译器不会告诉您有一个新的有效水果(例如Orange)当前未被wash 方法处理。
访问者
您可以使用访问者模式来实现:
并将访问者定义为:
然后,为您的洗漱箱定义访问者:
最后,您的代码可以是
Et voilà...
访问者模式有优点是,编译器会告诉您是否添加了另一个 Fruit(例如 Orange)并在其中编写了接受方法,以及您是否忘记更新 FruitVisitor 模式以支持它。
然后,Visitor 模式是可扩展的:您可以有一个 FruitVisitorWasher、一个 FruitVisitorEater、一个 FruitVisitorWhatever,添加它们而无需修改 Fruit 或 Apple、Peach 等。
不过,有一个陷阱,您必须在每个 Fruit 类中手动编写接受方法(这是一个复制/粘贴操作),因为正是这个方法完成了“了解”正确的水果类型的所有工作。
编辑
如果您选择 Quick'n'dirty 解决方案,Samuel Parsonage 的解决方案可能比我的更好:
如何使用通配符迭代此通用列表?
它利用了 Java 的反射(我是一名 C++ 编码员,因此反射并不作为自然的解决方案...我对此不好...)。我发现他的解决方案非常优雅,即使它有点奇怪(所有检查都将在运行时完成,所以你最好确保一切正常......再次,通过 C++ 背景:如果可以做某事,或者出现错误可以在编译时检测到,应尽可能避免在运行时移动它)
编辑 2
John Assymptoth 评论道:
因此,我将提供内联代码来证明 Wash() 预计不会在 Fruit 内部工作。
(我将 FruitVisitor 从抽象类更改为接口,这样更好)
让我们想象 for 循环位于 Foo 类的 bar 方法内部,该方法有自己的 Wash 方法:
您希望调用正确的 Wash 方法,因此上面的代码将无法正常工作。
让我们重用 FruitVisitor 模式来更正此代码。我们将在 bar 方法中使用匿名类:
现在,它可以工作了,并且 Fruits 中没有 Wash 方法。
请注意,此代码是针对 1.6 JVM 进行测试的,因此如果需要,我可以提供完整的代码。
Welcome to the world of Double Dynamic Dispatch.
AFAIK, you can't do it easily on Java. You can do it two ways : The quick'n'dirty, and the Visitor way:
Quick'n'dirty
You need to ask the type of the object, so you'll need a wash method on Fruit which will redirect the call to the right function according to its type:
The problem with the quick'n'dirty is that the compiler won't tell you there is a new valid Fruit (e.g. Orange) that is not currently handled by the wash method.
Visitor
You could use the Visitor pattern to Fruit:
And define the Visitor as :
And then, the Visitor for your wash case:
In the end, you code could be
Et voilà...
The Visitor pattern has the advantage the compiler will tell you if you added another Fruit (e.g. Orange) in which you coded an accept method and if you forgot to update the FruitVisitor pattern to support it.
And then, the Visitor pattern is extensible: You can have a FruitVisitorWasher, a FruitVisitorEater, a FruitVisitorWhatever, adding them without needing to modify Fruit nor Apple, Peach, etc..
One trap, though, you must manually write in each Fruit class the accept method (which is a copy/paste action) because it is this method which does all the work of "knowing" the right Fruit type.
Edit
If you go for the Quick'n'dirty solution, the Samuel Parsonage's solution could be even better than mine:
How to iterate over this generic List with wildcards?
which makes use of Java's reflection (I'm a C++ coder, so reflection doesn't come as a natural solution... My bad on this...). I find his solution quite elegant, even if it smells somehow (all the checks will be done in the runtime, so you'd better make sure everything's ok... Again, by C++ background: If something can be done, or an error can be detected at compile time, moving it at runtime should be avoided as much as possible)
Edit 2
John Assymptoth commented:
So I'll offer the inline code to prove wash() is not expected to be inside Fruit to work.
(I changed FruitVisitor from an abstract class into an interface, which is better)
Let's imagine the for loop is inside the bar method of the Foo class, which has its own wash method:
You want the right wash method called, so the code above won't work correctly.
Let's reuse the FruitVisitor pattern to correct this code. We will use an anonymous class inside the bar method:
Now, it works, and there are no wash method in Fruits.
Note that this code was tested against a 1.6 JVM, so I can provide the complete code if necessary.
这可以使用反射
来尝试这个
This is possible using reflection
Try this
尝试修改
为
然后使用wash方法中的第一个元素。
您仍然需要 Fruit 类中的 Wash() 方法。
Fruit 类中的 Wash 方法将是抽象的,应在子类中定义。
Try modifying
to
And then use the first element in wash method.
You still need to have the wash() method in Fruit class.
wash method in Fruit class will be abstract and should be defined in subclasses.