Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/325.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 如何使用通配符迭代此通用列表?_Java_Generics_Collections_Iteration_Wildcard - Fatal编程技术网

Java 如何使用通配符迭代此通用列表?

Java 如何使用通配符迭代此通用列表?,java,generics,collections,iteration,wildcard,Java,Generics,Collections,Iteration,Wildcard,我有一个扩展另一个类的对象列表: List<? extends Fruit> arguments; 如何将wash方法应用于参数中的所有元素?这不起作用,因为我的清洗方法不接受Fruit参数: for( Fruit f: arguments) this.wash( f); // the wash() method is not a member of Fruit 有没有办法解决这个问题,而不必使用伞式方法清洗(水果)?因为有几十种wash(?extends Fruit)方

我有一个扩展另一个类的对象列表:

List<? extends Fruit> arguments;
如何将wash方法应用于
参数中的所有元素?这不起作用,因为我的清洗方法不接受
Fruit
参数:

for( Fruit f: arguments)
    this.wash( f); // the wash() method is not a member of Fruit
有没有办法解决这个问题,而不必使用伞式方法清洗(水果)
?因为有几十种
wash(?extends Fruit)
方法


编辑:我说的“调用类”是一个访问者。我不能改变任何
水果
类/子类。我只能为访问者编程。这意味着不可能将
wash()
方法(或任何其他方法)添加到抽象类
Fruit

,这可以使用反射

试试这个

Method m=this.getClass().getMethod("wash", f.getClass());
m.invoke(this, f.getClass().cast(f));
尝试修改

void wash( Apple a);

void wash(列表欢迎来到虚拟世界

顺便说一句,在Java上做这件事并不容易。你可以用两种方法来做:quick'n'dirty和Visitor方法:

快脏 您需要询问对象的类型,因此需要对Fruit使用wash方法,该方法将根据其类型将调用重定向到正确的函数:

public void wash(Fruit f)
{
   if(f instanceof Apple)
   {
      wash((Apple) f) ;
   }
   else if(f instanceof Peach)
   {
      wash((Peach) f) ;
   }
   else
   {
      // handle the error, usually through an exception
   }
}
quick'n'dirty的问题在于编译器不会告诉您存在当前未通过wash方法处理的新有效水果(例如橙色)

访客 您可以使用访问者模式来实现:

public abstract class Fruit
{
   // etc.
   public abstract void accept(FruitVisitor v) ;
}

public class Apple extends Fruit
{
   // etc.
   public void accept(FruitVisitor v)
   {
      v.visit(this) ;
   }
}

public class Peach extends Fruit
{
   // etc.
   public void accept(FruitVisitor v)
   {
      v.visit(this) ;
   }
}
并将访客定义为:

public interface class FruitVisitor
{
   // etc.

   // Note that there are no visit method for Fruit
   // this is not an error

   public void visit(Apple a) ;
   public void visit(Peach p) ;
}
然后,你洗衣箱的访客:

public class FruitVisitorWasher : implements FruitVisitor
{
   // etc.

   // Note that there are no visit method for Fruit
   // this is not an error

   // Note, too, that you must provide a wash method in
   // FruitVisitorWasher (or use an anonymous class, as
   // in the example of the second edit to access the
   // wash method of the outer class)

   public void visit(Apple a)
   {
      wash(a) ;
   }

   public void visit(Peach p)
   {
      wash(p) ;
   }
}
最终,您的代码可能是

FruitVisitorWasher fvw = new FruitVisitorWasher() ;

for( Fruit f: arguments)
{
   f.accept(fvw) ;
}

访问者模式有一个优点,编译器会告诉您是否添加了另一个用于编码accept方法的水果(如Orange),以及是否忘记更新水果访问者模式以支持它

然后,访问者模式是可扩展的:您可以有一个水果访问者清洗机、一个水果访问者清洗机、一个水果访问者清洗机、一个水果访问者等等,添加它们时不需要修改水果、苹果、桃子等

不过,有一个陷阱,您必须在每个水果类中手动编写accept方法(这是一个复制/粘贴操作),因为正是这个方法完成了“知道”正确水果类型的所有工作

编辑 如果你选择快速的“肮脏”解决方案,塞缪尔牧师住宅的解决方案可能比我的更好:

这是利用java的反射(我是C++编码器,所以反射不是自然的解决方法…我的这个坏……)我发现他的解决方案很优雅,即使它闻起来有点难闻。(所有的检查都将在运行时完成,所以您最好确保一切都好……再次由C++背景:如果可以做某事,或者在编译时可以检测到错误,那么尽可能避免在运行时移动它)

编辑2 John Assymptoth评论道:

您编写的访问者模式不是一个选项,因为我无法将wash方法添加到水果中

因此,我将提供内联代码来证明wash()不应该在Fruit中工作

(我将FruitVisitor从抽象类更改为接口,这样更好)

让我们假设for循环在Foo类的bar方法中,它有自己的wash方法:

public class Foo
{
   public wash(Apple a) { /* etc. */ }
   public wash(Peach p) { /* etc. */ }
   public bar(List<? extends Fruit> arguments)
   {
      for( Fruit f: arguments)
      {
         wash(f) ; // we wand the right wash method called.
      }
   }
}
公共类Foo
{
公共厕所(苹果a){/*等。*/}
公共厕所(桃色p){/*等./}

public bar(Listwhy不想在水果类中使用wash()方法?@vishwanath我认为让水果自己清洗是没有意义的。wash方法不在水果类中。它在处理水果的类中。this.wash()。我编辑了原始帖子。它是多态性,你可以声明方法抽象,表示每个水果都有自己的清洗类型。@vishwanath:你不能为你的水果类和每个继承类添加一个“doSomething”方法,因为你的代码中会有一个水果的每次处理。首先,它是不可扩展的(作为您类的用户,我无法扩展它以添加我的“doSomethingElse”方法,而且无论如何,你只会用杂乱的方法污染你的类。使用访问者模式。保持你的类简单,并将代码保持在应该使用的地方。他希望它是多态的——有多个
wash
方法,他希望它根据类型选择正确的方法。注意,在使用“wash”时在继承类上重写的Fruit类中的方法在某种情况下是可以的,但这仍然是一个很大的先决条件:通常不能为每个本地处理在每个类上添加(虚拟)方法(如果只是因为不是所有类都可以很容易地修改为用户的特殊情况)。访问者模式的存在是为了优雅地处理这个问题。我相信这是最好、最简洁的解决方案。@gigadot:不是最好的解决方案,因为它太依赖于运行时(如果f不是水果,或者没有wash(橙色o)方法,会发生什么情况)?Java是一种强类型语言:编译器应该能够在这方面提供帮助。我有一种预感,这种代码可能非常慢(通过名称搜索方法…)。这仍然是最简洁的解决方案,而且我觉得它相当优雅。回答太棒了。我的抽象类已经有了一个accept方法,所以我不需要使用它,否则,这将是一个非常紧凑和有用的解决方案。谢谢。@gigadot:谢谢。我总是忘记如何正确编写访问者模式。虽然我理解它及其用法,我记不清确切的代码,因此我发现,如果读者不知道访问者,那么尽可能重新编写一个示例对读者和我都很有用。回答得很好。不幸的是,您编写的访问者模式不是一个选项,因为我无法将方法wash添加到水果中。“quick'n'dirty”解决方案是我记忆中的第一个,但有几十个水果并没有帮助。不过,塞缪尔·帕索纳奇的解决方案似乎正是我所需要的。谢谢你的帮助。@John Assymptoth:访客模式不需要清洗方法才能结出果实。我将编辑我的答案,提供一个“inlin”
FruitVisitorWasher fvw = new FruitVisitorWasher() ;

for( Fruit f: arguments)
{
   f.accept(fvw) ;
}
public class Foo
{
   public wash(Apple a) { /* etc. */ }
   public wash(Peach p) { /* etc. */ }
   public bar(List<? extends Fruit> arguments)
   {
      for( Fruit f: arguments)
      {
         wash(f) ; // we wand the right wash method called.
      }
   }
}
public class Foo
{
   public void wash(Apple a) { System.out.println("Apple") ; }
   public void wash(Peach p) { System.out.println("Peach") ; }
   
   public void bar(List<? extends Fruit> arguments)
   {
      FruitVisitor fv = new FruitVisitor()
      {
         public void visit(Apple a)
         {
            wash(a) ; // will call the wash method
                      // of the outer class (Foo)
         }

         public void visit(Peach p)
         {
            wash(p) ; // will call the wash method
                      // of the outer class (Foo)
         }
      } ;

      for(Fruit f: arguments)
      {
         f.accept(fv) ;
      }
   }
}