我主要使用Java,泛型相对较新。我一直读到Java做出了错误的决定,或者。net有更好的实现等等。
那么,c++, c#, Java在泛型上的主要区别是什么?利弊各有哪些?
我主要使用Java,泛型相对较新。我一直读到Java做出了错误的决定,或者。net有更好的实现等等。
那么,c++, c#, Java在泛型上的主要区别是什么?利弊各有哪些?
当前回答
Java和c#在它们的第一个语言版本之后都引入了泛型。然而,在引入泛型时,核心库的变化方式有所不同。c#的泛型不仅仅是编译器的魔法,因此不可能在不破坏向后兼容性的情况下泛化现有的库类。
例如,在Java中,现有的集合框架是完全泛化的。Java没有集合类的泛型版本和遗留的非泛型版本。在某种程度上,这要干净得多——如果你需要在c#中使用一个集合,那么使用非泛型版本的理由真的很少,但是那些遗留类仍然存在,使环境变得混乱。
另一个显著的区别是Java和c#中的Enum类。Java的Enum有这样一个看起来有点曲折的定义:
// java.lang.Enum Definition in Java
public abstract class Enum<E extends Enum<E>> implements Comparable<E>, Serializable {
(见Angelika Langer对为什么会这样的解释非常清楚。从本质上讲,这意味着Java可以提供从字符串到Enum值的类型安全访问:
// Parsing String to Enum in Java
Colour colour = Colour.valueOf("RED");
比较一下c#版本:
// Parsing String to Enum in C#
Colour colour = (Colour)Enum.Parse(typeof(Colour), "RED");
因为Enum在引入泛型之前就已经存在于c#中,所以如果不改变定义就会破坏现有的代码。因此,像集合一样,它以这种遗留状态保留在核心库中。
其他回答
跟进我之前的帖子。
模板是c++在智能感知上如此糟糕的主要原因之一,不管使用的是哪种IDE。由于模板专门化,IDE永远无法真正确定给定成员是否存在。考虑:
template <typename T>
struct X {
void foo() { }
};
template <>
struct X<int> { };
typedef int my_int_type;
X<my_int_type> a;
a.|
现在,光标位于指定的位置,IDE很难判断成员a是否具有或具有什么。对于其他语言,解析将是简单的,但对于c++,需要在此之前进行相当多的计算。
情况变得更糟。如果my_int_type也定义在类模板中呢?现在它的类型依赖于另一个类型参数。在这里,即使是编译器也会失败。
template <typename T>
struct Y {
typedef T my_type;
};
X<Y<int>::my_type> b;
经过一些思考,程序员会得出结论,这段代码与上面的相同:Y<int>::my_type解析为int,因此b应该是与a相同的类型,对吗?
错了。当编译器试图解析这条语句时,它实际上还不知道Y<int>::my_type !因此,它不知道这是一个类型。它可以是其他东西,例如成员函数或字段。这可能会导致歧义(尽管在本例中没有),因此编译器会失败。我们必须显式地告诉它我们引用了一个类型名:
X<typename Y<int>::my_type> b;
现在,代码编译完成。要了解这种情况下如何产生歧义,请考虑以下代码:
Y<int>::my_type(123);
这段代码语句完全有效,并告诉c++执行对Y<int>::my_type的函数调用。但是,如果my_type不是函数而是类型,则此语句仍然有效,并执行特殊的强制转换(函数样式强制转换),通常是构造函数调用。编译器无法判断我们的意思,所以我们必须在这里消除歧义。
在Java中,泛型只是编译器级别的,所以你得到:
a = new ArrayList<String>()
a.getClass() => ArrayList
注意,'a'的类型是数组列表,而不是字符串列表。所以香蕉列表的类型等于()猴子列表。
可以这么说。
注:我没有足够的观点来评论,所以请随意将此作为评论移动到适当的答案。
Contrary to popular believe, which I never understand where it came from, .net implemented true generics without breaking backward compatibility, and they spent explicit effort for that. You don't have to change your non-generic .net 1.0 code into generics just to be used in .net 2.0. Both the generic and non-generic lists are still available in .Net framework 2.0 even until 4.0, exactly for nothing else but backward compatibility reason. Therefore old codes that still used non-generic ArrayList will still work, and use the same ArrayList class as before. Backward code compatibility is always maintained since 1.0 till now... So even in .net 4.0, you still have to option to use any non-generics class from 1.0 BCL if you choose to do so.
所以我不认为java必须打破向后兼容性来支持真正的泛型。
11个月后,但我认为这个问题已经准备好了一些Java通配符的东西。
这是Java的一个语法特性。假设你有一个方法:
public <T> void Foo(Collection<T> thing)
假设您不需要在方法体中引用类型T。您声明了一个名称T,然后只使用了一次,那么为什么还要为它想一个名称呢?相反,你可以这样写:
public void Foo(Collection<?> thing)
问号要求编译器假装您声明了一个普通的命名类型参数,该参数只需要在该位置出现一次。
用通配符可以做的事情,用命名类型参数也可以做(在c++和c#中,这些事情总是这样做的)。
维基百科上有很多比较Java/ c#泛型和Java泛型/ c++模板的文章。关于泛型的主要文章似乎有点混乱,但它确实有一些很好的信息。