Java增强了for循环VS .NET foreach循环

时间:2012-04-11 07:27:53

标签: c# java polymorphism foreach

我有一个问题,我没有找到答案。 假设我们在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

这是因为卡车是汽车的子类,简单来说就意味着每辆卡车也是一辆汽车。但事情是,完成了类型检查,只从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中不起作用?

4 个答案:

答案 0 :(得分:8)

  

事情是,完成了类型检查,只从carList中选择了卡车。

不,不是。如果您的列表包含除Truck之外的任何内容,则在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<Car>。如果C#选择了foreach的Java方式,则必须编写

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

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

答案 1 :(得分:5)

在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
}

这样可以过滤掉可枚举的东西,只有卡车才能进入循环。

答案 2 :(得分:2)

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

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

长话短说 - 你不能写这样的代码 - 这是一个编译时错误。 你有一个汽车列表,你试图得到一个卡车列表 - 它甚至听起来不对 /

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

答案 3 :(得分:1)

程序将不会编译,因为您使用的是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
}