C++ 和 Java 中的“通用”类型有什么区别?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/36347/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What are the differences between "generic" types in C++ and Java?
提问by popopome
Java has generics and C++ provides a very strong programming model with template
s.
So then, what is the difference between C++ and Java generics?
Java 有泛型,C++ 提供了一个非常强大的带有template
s 的编程模型。那么,C++ 和 Java 泛型有什么区别呢?
采纳答案by Alexandru Nedelcu
There is a big difference between them. In C++ you don't have to specify a class or an interface for the generic type. That's why you can create truly generic functions and classes, with the caveat of a looser typing.
它们之间有很大的区别。在 C++ 中,您不必为泛型类型指定类或接口。这就是为什么您可以创建真正通用的函数和类,但要注意更宽松的类型。
template <typename T> T sum(T a, T b) { return a + b; }
The method above adds two objects of the same type, and can be used for any type T that has the "+" operator available.
上面的方法添加了两个相同类型的对象,并且可以用于任何具有“+”运算符可用的类型 T。
In Java you have to specify a type if you want to call methods on the objects passed, something like:
在 Java 中,如果要对传递的对象调用方法,则必须指定类型,例如:
<T extends Something> T sum(T a, T b) { return a.add ( b ); }
In C++ generic functions/classes can only be defined in headers, since the compiler generates different functions for different types (that it's invoked with). So the compilation is slower. In Java the compilation doesn't have a major penalty, but Java uses a technique called "erasure" where the generic type is erased at runtime, so at runtime Java is actually calling ...
在 C++ 中,泛型函数/类只能在头文件中定义,因为编译器为不同的类型(调用它的)生成不同的函数。所以编译比较慢。在 Java 中,编译没有重大损失,但 Java 使用一种称为“擦除”的技术,其中泛型类型在运行时被擦除,因此在运行时 Java 实际上是在调用......
Something sum(Something a, Something b) { return a.add ( b ); }
So generic programming in Java is not really useful, it's only a little syntactic sugar to help with the new foreach construct.
因此,Java 中的泛型编程并不是真正有用,它只是帮助新的 foreach 构造的一点语法糖。
EDIT:the opinion above on usefulness was written by a younger self. Java's generics help with type-safety of course.
编辑:上面关于有用性的意见是由年轻的自己写的。Java 的泛型当然有助于类型安全。
回答by Shog9
C++ has templates. Java has generics, which look kinda sorta like C++ templates, but they're very, very different.
C++ 有模板。Java 有泛型,看起来有点像 C++ 模板,但它们非常非常不同。
Templates work, as the name implies, by providing the compiler with a (wait for it...) template that it can use to generate type-safe code by filling in the template parameters.
顾名思义,模板的工作原理是为编译器提供一个(等待它...)模板,它可以通过填充模板参数来生成类型安全代码。
Generics, as I understand them, work the other way around: the type parameters are used by the compiler to verify that the code using them is type-safe, but the resulting code is generated without types at all.
泛型,正如我所理解的,反过来工作:编译器使用类型参数来验证使用它们的代码是类型安全的,但生成的代码根本没有类型。
Think of C++ templates as a really goodmacro system, and Java generics as a tool for automatically generating typecasts.
将 C++ 模板视为一个非常好的宏系统,将 Java 泛型视为自动生成类型转换的工具。
回答by Ferruccio
Java (and C#) generics seem to be a simple run-time type substitution mechanism.
C++ templates are a compile-time construct which give you a way to modify the language to suit your needs. They are actually a purely-functional language that the compiler executes during a compile.
Java(和 C#)泛型似乎是一种简单的运行时类型替换机制。
C++ 模板是一种编译时构造,它为您提供了一种修改语言以满足您的需要的方法。它们实际上是编译器在编译期间执行的纯函数式语言。
回答by KeithB
Another advantage of C++ templates is specialization.
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); }
Now, if you call sum with pointers, the second method will be called, if you call sum with non-pointer objects the first method will be called, and if you call sum
with Special
objects, the third will be called. I don't think that this is possible with Java.
现在,如果你用指针调用 sum ,第二个方法将被调用,如果你用非指针对象调用 sum ,第一个方法将被调用,如果你sum
用Special
对象调用,第三个方法将被调用。我认为 Java 不可能做到这一点。
回答by Konrad Rudolph
@Keith:
@基思:
That code is actually wrong and apart from the smaller glitches (template
omitted, specialization syntax looks differently), partial specialization doesn'twork on function templates, only on class templates. The code would however work without partial template specialization, instead using plain old overloading:
该代码实际上是错误的,除了较小的故障(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); }
回答by OscarRyz
Basically, AFAIK, C++ templates create a copy of the code for each type, while Java generics use exactly the same code.
基本上,AFAIK、C++ 模板为每种类型创建代码的副本,而 Java 泛型使用完全相同的代码。
Yes, you can saythat C++ template is equivalent to Java generic concept( although more properly would be to say Java generics are equivalent to C++ in concept )
是的,您可以说C++ 模板等同于 Java 泛型概念(尽管更恰当的说法是 Java 泛型在概念上等同于 C++)
If you are familiar with C++'s template mechanism, you might think that generics are similar, but the similarity is superficial. Generics do not generate a new class for each specialization, nor do they permit “template metaprogramming.”
如果你熟悉 C++ 的模板机制,你可能会认为泛型是相似的,但相似是表面的。泛型不会为每个专业化生成一个新类,也不允许“模板元编程”。
from: Java Generics
来自:Java泛型
回答by cletus
Java Generics are massivelydifferent to C++ templates.
Java泛型是大量C ++模板不同。
Basically in C++ templates are basically a glorified preprocessor/macro set (Note:since some people seem unable to comprehend an analogy, I'm not saying template processing is a macro). In Java they are basically syntactic sugar to minimize boilerplate casting of Objects. Here is a pretty decent introduction to C++ templates vs Java generics.
基本上在 C++ 中,模板基本上是一个美化的预处理器/宏集(注意:因为有些人似乎无法理解类比,我并不是说模板处理是一个宏)。在 Java 中,它们基本上是一种语法糖,以最大限度地减少对象的样板转换。这是对 C++ 模板与 Java 泛型的相当不错的介绍。
To elaborate on this point: when you use a C++ template, you're basically creating another copy of the code, just as if you used a #define
macro. This allows you to do things like have int
parameters in template definitions that determine sizes of arrays and such.
详细说明这一点:当您使用 C++ 模板时,您基本上是在创建代码的另一个副本,就像您使用#define
宏一样。这允许您执行诸如int
在模板定义中设置参数来确定数组大小等操作。
Java doesn't work like that. In Java all objects extent from java.lang.Objectso, pre-Generics, you'd write code like this:
Java 不是那样工作的。在 Java 中,所有对象都来自java.lang.Object,因此,在泛型之前,您将编写如下代码:
public class PhoneNumbers {
private Map phoneNumbers = new HashMap();
public String getPhoneNumber(String name) {
return (String)phoneNumbers.get(name);
}
...
}
because all the Java collection types used Object as their base type so you could put anything in them. Java 5 rolls around and adds generics so you can do things like:
因为所有 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);
}
...
}
And that's all Java Generics are: wrappers for casting objects. That's because Java Generics aren't refined. They use type erasure. This decision was made because Java Generics came along so late in the piece that they didn't want to break backward compatibility (a Map<String, String>
is usable whenever a Map
is called for). Compare this to .Net/C# where type erasure isn't used, which leads to all sorts of differences (e.g. you can use primitive types and IEnumerable
and IEnumerable<T>
bear no relation to each other).
这就是 Java 泛型的全部内容:用于转换对象的包装器。那是因为 Java 泛型没有经过改进。他们使用类型擦除。做出这个决定是因为 Java 泛型出现得太晚了,以至于他们不想破坏向后兼容性(Map<String, String>
只要需要a,就可以使用 a Map
)。与此相比,净/ C#在不使用类型擦除,从而导致各种各样的差异(例如,你可以使用基本类型和IEnumerable
和IEnumerable<T>
不承担任何关于彼此)。
And a class using generics compiled with a Java 5+ compiler is usable on JDK 1.4 (assuming it doesn't use any other features or classes that require Java 5+).
使用 Java 5+ 编译器编译的泛型类可以在 JDK 1.4 上使用(假设它不使用任何其他需要 Java 5+ 的特性或类)。
That's why Java Generics are called syntactic sugar.
这就是 Java 泛型被称为语法糖的原因。
But this decision on how to do generics has profound effects so much so that the (superb) Java Generics FAQhas sprung up to answer the many, many questions people have about Java Generics.
但是这个关于如何进行泛型的决定具有深远的影响,以至于(极好的)Java 泛型常见问题解答如雨后春笋般涌现,回答了人们对 Java 泛型的许多问题。
C++ templates have a number of features that Java Generics don't:
C++ 模板具有许多 Java 泛型没有的特性:
Use of primitive type arguments.
For example:
template<class T, int i> class Matrix { int T[i][i]; ... }
Java does not allow the use of primitive type arguments in generics.
Use of default type arguments, which is one feature I miss in Java but there are backwards compatibility reasons for this;
- Java allows bounding of arguments.
使用原始类型参数。
例如:
template<class T, int i> class Matrix { int T[i][i]; ... }
Java 不允许在泛型中使用原始类型参数。
使用默认类型参数,这是我在 Java 中怀念的一项功能,但有向后兼容的原因;
- Java 允许参数的边界。
For example:
例如:
public class ObservableList<T extends List> {
...
}
It really does need to be stressed that template invocations with different arguments really are different types. They don't even share static members. In Java this is not the case.
确实需要强调的是,具有不同参数的模板调用确实是不同的类型。他们甚至不共享静态成员。在 Java 中,情况并非如此。
Aside from the differences with generics, for completeness, here is a basic comparison of C++ and Java(and another one).
除了与泛型的差异之外,为了完整起见,这里是C++ 和 Java(以及另一个)的基本比较。
And I can also suggest Thinking in Java. As a C++ programmer a lot of the concepts like objects will be second nature already but there are subtle differences so it can be worthwhile to have an introductory text even if you skim parts.
我也可以建议Thinking in Java。作为一名 C++ 程序员,很多像对象这样的概念已经成为第二天性,但存在细微差别,因此即使您略读部分,也值得拥有介绍性文本。
A lot of what you'll learn when learning Java is all the libraries (both standard--what comes in the JDK--and nonstandard, which includes commonly used things like Spring). Java syntax is more verbose than C++ syntax and doesn't have a lot of C++ features (e.g. operator overloading, multiple inheritance, the destructor mechanism, etc) but that doesn't strictly make it a subset of C++ either.
在学习 Java 时,您将学到的很多东西都是所有的库(包括标准的——JDK 中的内容——和非标准的,包括常用的东西,比如 Spring)。Java 语法比 C++ 语法更冗长,并且没有很多 C++ 特性(例如运算符重载、多重继承、析构函数机制等),但这也不能严格使它成为 C++ 的子集。
回答by Julien Chastang
There is a great explanation of this topic in Java Generics and CollectionsBy Maurice Naftalin, Philip Wadler. I highly recommend this book. To quote:
Maurice Naftalin, Philip Wadler在Java 泛型和集合中对此主题有很好的解释。我强烈推荐这本书。报价:
Generics in Java resemble templates in C++. ... The syntax is deliberately similar and the semantics are deliberately different. ... Semantically, Java generics are defined by erasure, where as C++ templates are defined by expansion.
Java 中的泛型类似于 C++ 中的模板。...语法故意相似,语义故意不同。... 从语义上讲,Java 泛型由擦除定义,而 C++ 模板由扩展定义。
Please read the full explanation here.
请在此处阅读完整说明。
(source: oreilly.com)
(来源:oreilly.com)
回答by KeithB
Another feature that C++ templates have that Java generics don't is specialization. That allows you to have a different implementation for specific types. So you can, for example, have a highly optimized version for an int, while still having a generic version for the rest of the types. Or you can have different versions for pointer and non-pointer types. This comes in handy if you want to operate on the dereferenced object when handed a pointer.
C++ 模板具有而 Java 泛型没有的另一个特性是专门化。这允许您对特定类型有不同的实现。因此,例如,您可以为int提供一个高度优化的版本,同时仍然为其余类型提供一个通用版本。或者您可以为指针和非指针类型使用不同的版本。如果您想在传递指针时对取消引用的对象进行操作,这将派上用场。
回答by MigMit
Templates are nothing but a macro system. Syntax sugar. They are fully expanded before actual compilation (or, at least, compilers behave as if it were the case).
模板只不过是一个宏系统。语法糖。它们在实际编译之前被完全扩展(或者,至少,编译器的行为就像是这样)。
Example:
例子:
Let's say we want two functions. One function takes two sequences (list, arrays, vectors, whatever goes) of numbers, and returns their inner product. Another function takes a length, generates two sequences of that length, passes them to the first function, and returns it's result. The catch is that we might make a mistake in the second function, so that these two functions aren't really of the same length. We need the compiler to warn us in this case. Not when the program is running, but when it's compiling.
假设我们想要两个函数。一个函数接受两个数字序列(列表、数组、向量等等),并返回它们的内积。另一个函数接受一个长度,生成该长度的两个序列,将它们传递给第一个函数,并返回它的结果。问题是我们可能会在第二个函数中出错,因此这两个函数的长度实际上并不相同。在这种情况下,我们需要编译器来警告我们。不是在程序运行时,而是在编译时。
In Java you can do something like this:
在 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");
}
}
}
In C# you can write almost the same thing. Try to rewrite it in C++, and it won't compile, complaining about infinite expansion of templates.
在 C# 中,您几乎可以编写相同的内容。尝试用C++重写它,它不会编译,抱怨模板无限扩展。