我有一个问题,我没有找到答案。 假设我们在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中不起作用?
答案 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
}