Java具有泛型,C ++提供了一个非常强大的编程模型template
。
那么,C ++和Java泛型之间的区别是什么?
答案 0 :(得分:128)
他们之间有很大的不同。在C ++中,您不必为泛型类型指定类或接口。这就是为什么你可以创建真正通用的函数和类,并注意更宽松的输入。
template <typename T> T sum(T a, T b) { return a + b; }
上述方法添加了两个相同类型的对象,可用于任何具有“+”运算符的类型T.
在Java中,如果要在传递的对象上调用方法,则必须指定类型,如:
<T extends Something> T sum(T a, T b) { return a.add ( b ); }
在C ++中,泛型函数/类只能在头文件中定义,因为编译器会为不同类型(调用它)生成不同的函数。因此编译速度较慢。在Java中,编译没有重大损失,但Java使用一种称为“擦除”的技术,其中泛型类型在运行时被擦除,因此在运行时Java实际上正在调用...
Something sum(Something a, Something b) { return a.add ( b ); }
因此Java中的泛型编程并不是真正有用,它只是帮助新foreach构造的一点点语法糖。
编辑:以上关于实用性的观点是由年轻人自己写的。 Java的泛型当然有助于类型安全。
答案 1 :(得分:117)
Java Generics 大量与C ++模板不同。
基本上在C ++模板中基本上是一个美化的预处理器/宏集(注意:,因为有些人似乎无法理解一个类比,我不是说模板处理是一个宏)。在Java中,它们基本上是语法糖,以最小化对象的样板转换。这是一个相当不错的introduction to C++ templates vs Java generics。
详细说明这一点:当您使用C ++模板时,您基本上是在创建代码的另一个副本,就像使用#define
宏一样。这允许您在模板定义中执行诸如int
参数之类的操作,以确定数组的大小等。
Java不能那样工作。在Java中,所有对象的范围都来自java.lang.Object所以,在Generics之前,您可以编写如下代码:
public class PhoneNumbers {
private Map phoneNumbers = new HashMap();
public String getPhoneNumber(String name) {
return (String)phoneNumbers.get(name);
}
...
}
因为所有Java集合类型都使用Object作为其基类型,因此您可以在其中放置任何内容。 Java 5滚动并添加泛型,以便您可以执行以下操作:
public class PhoneNumbers {
private Map<String, String> phoneNumbers = new HashMap<String, String>();
public String getPhoneNumber(String name) {
return phoneNumbers.get(name);
}
...
}
所有Java Generics都是:用于投射对象的包装器。那是因为Java Generics没有改进。他们使用类型擦除。做出这个决定是因为Java Generics在这篇文章中出现这么晚,他们不想破坏向后兼容性(只要Map<String, String>
被调用就可以使用Map
。将此与.Net / C#进行比较,其中未使用类型擦除,这会导致各种差异(例如,您可以使用原始类型,IEnumerable
和IEnumerable<T>
彼此之间没有任何关系。“ / p>
使用Java 5+编译器编译的泛型的类可以在JDK 1.4上使用(假设它不使用任何其他需要Java 5 +的特性或类)。
这就是Java Generics被称为syntactic sugar的原因。
但是这个关于如何做泛型的决定已经产生了深远的影响,以至于(精湛的)Java Generics FAQ如雨后春笋般出现,回答了人们对Java泛型的许多问题。
C ++模板具有Java Generics不具备的许多功能:
使用基本类型参数。
例如:
template<class T, int i>
class Matrix {
int T[i][i];
...
}
Java不允许在泛型中使用原始类型参数。
default type arguments的使用,这是我在Java中遗漏的一个功能,但有相反的兼容性原因;
例如:
public class ObservableList<T extends List> {
...
}
确实需要强调的是,具有不同参数的模板调用确实是不同的类型。他们甚至不共享静态成员。在Java中,情况并非如此。
除了与泛型的差异外,为了完整性,这里有basic comparison of C++ and Java(和another one)。
我也可以建议Thinking in Java。作为一名C ++程序员,很多像对象这样的概念已经是第二天性,但是存在细微差别,所以即使你浏览部分内容也可以有一个介绍性的文本。
学习Java时学到的很多东西都是库(都是标准的 - JDK中的内容 - 和非标准的,包括常用的东西,如Spring)。 Java语法比C ++语法更冗长,并且没有很多C ++特性(例如运算符重载,多重继承,析构机制等),但这并不严格使它成为C ++的子集。
答案 2 :(得分:76)
C ++有模板。 Java有泛型,看起来有点像C ++模板,但它们非常非常不同。
模板工作,顾名思义,通过为编译器提供(等待它......)模板,它可以通过填充模板参数来生成类型安全的代码。
泛型,正如我所理解的那样,反过来工作:编译器使用类型参数来验证使用它们的代码是否类型安全,但生成的代码根本没有类型生成。
将C ++模板视为非常好的宏系统,将Java泛型视为自动生成类型转换的工具。
答案 3 :(得分:15)
C ++模板具有Java泛型不具备的另一个特性是专业化。这允许您对特定类型具有不同的实现。例如,您可以为 int 提供高度优化的版本,同时仍然具有其他类型的通用版本。或者,您可以为指针和非指针类型使用不同的版本。如果你想在传递指针时对取消引用的对象进行操作,这就派上用场了。
答案 4 :(得分:13)
Java Generics and Collections对此主题有很好的解释 作者:Maurice Naftalin,Philip Wadler。我强烈推荐这本书。引用:
Java中的泛型类似于模板 C ++。 ...语法是故意的 类似和语义是 故意不同。 ... 在语义上,Java泛型是 由擦除定义,在C ++中 模板由扩展定义。
请阅读完整说明here。
(来源:oreilly.com)
答案 5 :(得分:5)
基本上,AFAIK,C ++模板为每种类型创建代码的副本,而Java泛型使用完全相同的代码。
是的,你可以说 C ++模板等同于Java泛型概念(尽管更合适的是说Java泛型在概念上等同于C ++)
如果您熟悉C ++的模板机制,您可能会认为泛型相似,但相似性是肤浅的。泛型不为每个专业化生成新类,也不允许“模板元编程”。
答案 6 :(得分:3)
Java(和C#)泛型似乎是一种简单的运行时类型替换机制。
C ++模板是一个编译时构造,它为您提供了一种修改语言以满足您需求的方法。它们实际上是编译器在编译期间执行的纯函数语言。
答案 7 :(得分:3)
C ++模板的另一个优点是专业化。
template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }
Special sum(const Special& a, const Special& b) { return a.plus(b); }
现在,如果你使用指针调用sum,则会调用第二个方法,如果你用非指针对象调用sum,将调用第一个方法,如果你用sum
调用Special
对象,第三个将被调用。我不认为这可以用Java。
答案 8 :(得分:2)
我将用一句话概括:模板创建新类型,泛型限制现有类型。
答案 9 :(得分:1)
@Keith:
该代码实际上是错误的,除了较小的毛刺(template
省略,专业化语法看起来不同),部分特化不在函数模板上工作,仅在类模板上工作。然而,代码可以在没有部分模板专门化的情况下工作,而是使用普通的旧重载:
template <typename T> T sum(T a, T b) { return a + b; }
template <typename T> T sum(T* a, T* b) { return (*a) + (*b); }
答案 10 :(得分:1)
下面的答案来自破解编码面试第13章的解决方案,我认为这很好。
Java泛型的实现源于“类型擦除:”的思想,该技术在将源代码转换为Java虚拟机(JVM)字节码时消除了参数化类型。例如,假设您具有以下Java代码:
Vector<String> vector = new Vector<String>();
vector.add(new String("hello"));
String str = vector.get(0);
在编译过程中,此代码被重写为:
Vector vector = new Vector();
vector.add(new String("hello"));
String str = (String) vector.get(0);
Java泛型的使用并没有真正改变我们的功能。它使事情变得更漂亮。因此,Java泛型有时称为“语法糖:”。
这与C ++完全不同。在C ++中,模板本质上是美化的宏集,编译器为每种类型创建模板代码的新副本。事实证明,MyClass实例不会与MyClass共享静态变量。但是,MyClass的两个实例将共享一个静态变量。
/*** MyClass.h ***/
template<class T> class MyClass {
public:
static int val;
MyClass(int v) { val v;}
};
/*** MyClass.cpp ***/
template<typename T>
int MyClass<T>::bar;
template class MyClass<Foo>;
template class MyClass<Bar>;
/*** main.cpp ***/
MyClass<Foo> * fool
MyClass<Foo> * foo2
MyClass<Bar> * barl
MyClass<Bar> * bar2
new MyClass<Foo>(10);
new MyClass<Foo>(15);
new MyClass<Bar>(20);
new MyClass<Bar>(35);
int fl fool->val; // will equal 15
int f2 foo2->val; // will equal 15
int bl barl->val; // will equal 35
int b2 bar2->val; // will equal 35
在Java中,无论类型参数不同,静态变量在MyClass实例之间共享。
Java泛型和C ++模板还有许多其他区别。这些包括:
答案 11 :(得分:0)
模板只是一个宏系统。语法糖。它们在实际编译之前完全展开(或者,至少编译器的行为就像是这样)。
示例:
假设我们需要两个功能。一个函数接受两个序列(列表,数组,向量,无论如何),并返回它们的内积。另一个函数需要一个长度,生成该长度的两个序列,将它们传递给第一个函数,并返回它的结果。问题是我们可能在第二个函数中犯了一个错误,因此这两个函数的长度实际上并不相同。在这种情况下,我们需要编译器警告我们。不是在程序运行时,而是在程序编译时。
在Java中你可以这样做:
import java.io.*;
interface ScalarProduct<A> {
public Integer scalarProduct(A second);
}
class Nil implements ScalarProduct<Nil>{
Nil(){}
public Integer scalarProduct(Nil second) {
return 0;
}
}
class Cons<A implements ScalarProduct<A>> implements ScalarProduct<Cons<A>>{
public Integer value;
public A tail;
Cons(Integer _value, A _tail) {
value = _value;
tail = _tail;
}
public Integer scalarProduct(Cons<A> second){
return value * second.value + tail.scalarProduct(second.tail);
}
}
class _Test{
public static Integer main(Integer n){
return _main(n, 0, new Nil(), new Nil());
}
public static <A implements ScalarProduct<A>>
Integer _main(Integer n, Integer i, A first, A second){
if (n == 0) {
return first.scalarProduct(second);
} else {
return _main(n-1, i+1,
new Cons<A>(2*i+1,first), new Cons<A>(i*i, second));
//the following line won't compile, it produces an error:
//return _main(n-1, i+1, first, new Cons<A>(i*i, second));
}
}
}
public class Test{
public static void main(String [] args){
System.out.print("Enter a number: ");
try {
BufferedReader is =
new BufferedReader(new InputStreamReader(System.in));
String line = is.readLine();
Integer val = Integer.parseInt(line);
System.out.println(_Test.main(val));
} catch (NumberFormatException ex) {
System.err.println("Not a valid number");
} catch (IOException e) {
System.err.println("Unexpected IO ERROR");
}
}
}
在C#中你几乎可以写同样的东西。尝试用C ++重写它,它不会编译,抱怨模板的无限扩展。
答案 12 :(得分:0)
我想在这里引用askanydifference:
C ++和Java之间的主要区别在于它们对平台的依赖性。 C ++是平台相关的语言,而Java是平台无关的语言。
以上陈述是C ++能够提供真正的泛型类型的原因。尽管Java确实进行了严格的检查,因此它们不允许像C ++那样使用泛型。