Java 泛型与 C++ 模板有何不同?为啥我不能使用 int 作为参数?
Posted
技术标签:
【中文标题】Java 泛型与 C++ 模板有何不同?为啥我不能使用 int 作为参数?【英文标题】:How are Java generics different from C++ templates? Why can't I use int as a parameter?Java 泛型与 C++ 模板有何不同?为什么我不能使用 int 作为参数? 【发布时间】:2009-06-15 13:32:13 【问题描述】:我正在尝试创建
ArrayList<int> myList = new ArrayList<int>();
在 Java 中,但这不起作用。
有人可以解释为什么int
作为类型参数不起作用吗?
将Integer
类用于int
原始作品,但有人可以解释为什么int
不被接受吗?
Java 1.6 版
【问题讨论】:
***.com/questions/31693 【参考方案1】:Java 泛型与 C++ 模板如此不同,因此我不打算在此处列出它们的区别。 (详情请参阅What are the differences between “generic” types in C++ and Java?。)
在这种特殊情况下,问题在于您不能将原语用作泛型类型参数(请参阅JLS §4.5.1:“类型参数可能是引用类型或通配符。”)。
但是,由于自动装箱,您可以执行以下操作:
List<Integer> ints = new ArrayList<Integer>();
ints.add(3); // 3 is autoboxed into Integer.valueOf(3)
这样可以消除一些痛苦。不过,这肯定会损害运行时效率。
【讨论】:
【参考方案2】:int
不起作用的原因是您不能将原始类型用作 Java 中的泛型参数。
至于您的实际问题,C++ 模板与 Java 泛型有何不同,答案是它们真的非常不同。这些语言本质上应用完全不同的方法来实现相似最终效果。
Java 倾向于关注泛型的定义。即,仅通过考虑泛型中的代码来检查泛型定义的有效性。如果参数没有得到适当的约束,就不能对它们执行某些操作。不考虑最终调用它的实际类型。
C++ 正好相反。仅对模板本身进行最少的验证。它实际上只需要可解析即可被认为是有效的。定义的实际正确性是在使用模板的地方完成的。
【讨论】:
+1 勇敢地尝试解释差异 :-) (尽管我可能会对 C++ 模板的最小验证提出问题 - 任何不依赖于模板参数的代码都经过全面检查) 另外,众所周知,C++ 社区已经付出了很多努力来解决对依赖模板代码的最小验证的担忧(尤其是当它导致神秘的错误消息时)。 C++0x 将具有可以受“概念”约束的模板,并且在首次遇到定义时将完全验证其定义(错误消息将更有意义)。当然,C++0x 也会有不受约束的模板(它们有自己的优势:)。 @Faisal:为了保持最新,我可能应该提一下,因为您的评论“概念”已从 C++0x(现在可能是 C++0B)中删除。容我们说,这不是一个普遍受欢迎的决定。 为了让它再次保持最新状态,它们计划被包含在 C++17 中,但再次没有被接受。从版本 6 开始,它们作为编译器扩展包含在 GCC 中。【参考方案3】:它们是非常不同的概念,可以用来执行一些但不是所有相同的任务。正如其他回复中所说,要了解所有差异需要相当多的时间,但这是我认为的粗略。
泛型允许通过泛型容器的单个实例化来实现运行时多态容器。在 Java 中,所有(非原始)对象都是引用,并且所有引用的大小相同(并且具有一些相同的接口),因此可以由字节码处理。然而,只有字节码实例化的必要含义是类型擦除器;您无法分辨容器是用哪个类实例化的。这在 C++ 中不起作用,因为对象模型根本不同,其中对象并不总是被引用。
模板允许通过多个实例化编译时多态容器(以及通过在 c++ 类型系统上提供(当前弱类型)语言的模板元编程。)。这允许对给定类型进行专门化,缺点是可能需要多个编译实例化而导致“代码膨胀”。
模板比泛型更强大;前者实际上是嵌入在 C++ 中的另一种语言,而据我所知,后者仅在容器中有用
【讨论】:
Bolts Tasks 是泛型在容器之外有用的一个有趣例子。【参考方案4】:主要区别在于它们的实现方式,但它们的名称准确地描述了它们的实现。
模板的行为类似于模板。所以,如果你写:
template<typename T>
void f(T s)
std::cout << s << '\n';
...
int x = 0;
f(x);
...
编译器应用模板,因此最终编译器将代码视为:
void f_generated_with_int(int s)
std::cout << s << '\n';
...
int x = 0;
f_generated_with_int(x);
...
因此,对于用于调用f
的每种类型,都会“生成”一个新代码。
另一方面,泛型只进行类型检查,但随后所有类型信息都将被删除。所以,如果你写:
class X<T>
private T x;
public T getX() return x;
public void setX(T x) this.x = x;
...
Foo foo = new Foo();
X<Foo> x = new X<>();
x.setX(foo);
foo = x.getX();
...
Java 像这样编译它:
class X
private Object x;
public Object getX() return x;
public void setX(Object x) this.x = x;
...
Foo foo = new Foo();
X x = new X();
x.setX(foo);
foo = (Foo)x.getX();
...
最后:
模板需要对模板化函数的每次调用进行实例化(在每个 .cpp 文件的编译中),因此模板的编译速度较慢 对于泛型,您不能使用原语,因为它们不是Object
,所以泛型的通用性较差
【讨论】:
【参考方案5】:您不能在 Java 中使用原语作为类型参数。 Java 的泛型值得通过类型擦除,这意味着编译器会检查您是否使用了定义它们的类型,但是在编译时,所有内容都被视为对象。由于 int 和其他原语不是对象,因此不能使用它们。相反,请使用整数。
【讨论】:
【参考方案6】:那是因为 int 是一个原语,它是一个known issue。
如果你真的想要,你可以继承/编写你自己的集合来做到这一点。
【讨论】:
@z 链接说This bug is not available
【参考方案7】:
您可以尝试来自 GNU Trove 的 TIntArraList,它的作用类似于 int 值的 ArrayList。
【讨论】:
【参考方案8】:对于您的问题,Java 对象在某种程度上等同于 C++ 中的指针。
Java 会进行垃圾收集,因为这些动态对象在某些时候会“看不见”(不再被指向),然后需要进行空间清理。
int
被识别为原始类型,因此无法返回 null
,这就是 Java 泛型不能接受原始类型的原因。为了表明元素没有以Map
、Set
、List
的形式存储在Java 容器中,方法将返回null
。那如果不能回null
,你还回什么?
对于std::array
,静态和动态数组,C++ 强制您定义默认构造函数,这是因为 C++ 数组是类型数组,而不是 Java 中的指针数组。您必须指出哪个默认值(Java 中的null
值)将采用这种结构中的对象。
想一想,在 Java 中,数组中的任何对象默认为 null
,在 C++ 中,除非您声明一个指针数组并将所有指针设置为 0x0
或(最好)为 nullptr
.
【讨论】:
以上是关于Java 泛型与 C++ 模板有何不同?为啥我不能使用 int 作为参数?的主要内容,如果未能解决你的问题,请参考以下文章