【问题标题】:Java enhanced for loop VS .NET foreach loopJava 增强的 for 循环 VS .NET foreach 循环
【发布时间】:2012-04-11 07:27:52
【问题描述】:

我有一个问题没有找到答案。 假设我们在 java 或 c# 中有以下代码:

class Car {
   /* car stuff */
}

然后在 Java 中

class Truck extends Car {
   /* truck stuff */
}

和 C#

class Truck : Car {
   /* truck stuff again */
}

在 C# 中,以下工作正常:

List<Car> carList = new List<Car>();
//add some objects to the collection
foreach(Truck t in carList)
     //do stuff with only the Truck objects in the carList collection

之所以可行,是因为 Truck 是 Car 的子类,简单来说,这意味着每辆 Truck 也是 Car。但问题是,类型检查已完成,并且仅从 carList 中选择了卡车。

如果我们在 Java 中尝试同样的事情:

List<Car> carList = new ArrayList<Car>();
//add some objects to the collection
for(Truck t : carList)
     //**PROBLEM**

由于增强循环内的代码,代码甚至无法编译。相反,我们必须这样做才能获得相同的效果:

for(Car t : carList)
   if(t instanceof Car)
      //cast t to Truck and do truck stuff with it

这与在 C# 中没有任何问题的想法相同,但在 Java 中您需要额外的代码。甚至语法几乎相同! 它在 Java 中不起作用是有原因的吗?

【问题讨论】:

  • 这在 C# 中也不是很干净,因为每辆卡车都是汽车,但不是每辆汽车都是卡车……在 C# 中,转换是在运行时尝试的,但你应该非常小心。 Java 似乎对此更加谨慎,希望程序员先检查一下(当然,如果您知道自己在做什么,C# 版本确实会增加一些好处)。
  • 在 C# 中检查每个对象的类型。如果是 Truck,则进入 foreach 循环的主体,否则不进入。这有什么不干净的?我检查它(Java)或运行时检查它(C#)它是相同的最终结果。
  • Java Generics 允许清楚地指定对象,但如果您想要包含任何 Car 的列表,那么您可以编写类似这样的内容。列表 carList = new ArrayList();
  • @Florian Rappl,大多数程序员不知道他们在做什么,所以我怀疑从长远来看这是一个好处。 Java 设计者有简单的原则尽快找到总线。在这种情况下,运行时可能会发生异常。在 Java 中编译时。
  • 只为卡车输入 foreach 循环。例如,如果您在该列表中有总线,则运行时将引发异常。

标签: c# java polymorphism foreach


【解决方案1】:

问题是,类型检查已完成,并且仅从 carList 中选择了卡车。

不,不是。如果您的列表包含除 Trucks 之外的任何内容,则 C# 中将发生运行时异常。基本上,在 C# 中,如下

foreach(Truck t in carList) {
    ...
}

表现得像

foreach(object _t in carList) {
    Truck t = (Truck)_t;        // throws an InvalidCastException if _t is not a Truck
    ...
}

另一方面,Java 变体是类型安全的:您必须自己进行类型转换和类型检查。


那么,为什么 Java 和 C# 的行为不同呢?这是我的猜测:

C# 在泛型之前有 foreach 关键字。因此,不可能有List&lt;Car&gt;。如果 C# 选择了foreach 的 Java 方式,你就得写

foreach(object _c in myArraylistContainingOnlyCars) {
    Car c = (Car)_c;
    // do something with c
}

这很烦人。另一方面,扩展的 for 循环和泛型是在同一版本(Java 5)的 Java 中引入的,因此不需要自动转换。

【讨论】:

  • 所以你的意思是如果列表中没有 Truck 对象,就会抛出异常?
  • 不行,如果里面有非卡车对象,会抛出异常
  • 试一试,你是对的...应该重温我的 C#,谢谢你的回答!
【解决方案2】:

在 C# 中,当你编写时

foreach(Truck t in carList)

编译器理解的是(大致):

var enumerator = carList.GetEnumerator();

while (enumerator.MoveNext())
{
    Truck t = (Truck)enumerator.Current;
    // do stuff
}

如果 carList 包含非卡车汽车,则分配将在运行时失败。

您可以尝试以下控制台应用程序来说明:

namespace ConsoleApplication1
{
    class Car
    {
    }

    class Truck: Car
    {
    }

    class Program
    {
        static void Main(string[] args)
        {
            Car[] cars = new[] { new Car(), new Truck() };

            foreach (Truck t in cars)
            {
                Console.WriteLine("1 truck");
            }

            Console.Read();
        }
    }
}

C# 和 java 编译器在这里表现不同,因为在设计语言时做出了不同的选择。奇怪的是,“尽可能快地失败,如果可能的话在编译时”是 C# 规范中的常见准则。这是一个未应用它的情况,尽管我喜欢 C# 语言,但我更喜欢 java 行为。

但是,在 C# 中,linq 提供了一种简单的方法来实现您认为的功能:

foreach (Truck t in carList.OfType<Truck>())
{
    //do stuff
}

这样,可枚举项将被过滤,只有卡车会进入循环。

【讨论】:

    【解决方案3】:
    List<Car> carList = new ArrayList<Car>();
    //add some objects to the collection
    for(Truck t : carList)
         //**PROBLEM**
    

    Java 5 中引入的 Java 泛型与 .NET 泛型有点不同。

    长话短说——你不能写这样的代码——这是一个编译时错误。 你有一个汽车清单,然后你试图得到一个卡车清单——这听起来甚至是错误的/

    正确的方法是通过Car 接口使用Truck 类。如果你不能(没有足够的方法或方法有不同的含义),那么你可以过滤列表(在迭代之前)或者..重新设计你的类

    【讨论】:

      【解决方案4】:

      程序不会编译,因为您使用的是 Truck 类而不是 Car。

      不需要这个

      for(Car t : carList) {
          if(t instanceof Car)
              // Do stuff here
      }
      

      你可以这样做:

      for(Car t : carList) {
          // Do stuff here
      }
      

      如果你这样做,它会起作用:

      List<Truck> truckList = new ArrayList<Truck>();
      
      for(Car car : truckList) {
          // Do stuff here
      }
      

      或者这个:

      List<Car> carList = new ArrayList<Car>();
      
      for (Iterator<Car> it = carList.iterator(); it.hasNext();) {
          Truck car = (Truck) it.next();
          // Do stuff here
      }
      

      而不是这个:

      List<Car> carList = new ArrayList<Car>();
      
      for(Truck truck : carList) {
          // Do stuff here
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2012-10-02
        • 2011-01-20
        • 1970-01-01
        • 2013-06-14
        • 2014-02-23
        • 1970-01-01
        • 2016-03-23
        相关资源
        最近更新 更多