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 泛型不能接受原始类型的原因。为了表明元素没有以MapSetList 的形式存储在Java 容器中,方法将返回null。那如果不能回null,你还回什么?

对于std::array,静态和动态数组,C++ 强制您定义默认构造函数,这是因为 C++ 数组是类型数组,而不是 Java 中的指针数组。您必须指出哪个默认值(Java 中的null 值)将采用这种结构中的对象。

想一想,在 Java 中,数组中的任何对象默认为 null,在 C++ 中,除非您声明一个指针数组并将所有指针设置为 0x0 或(最好)为 nullptr .

【讨论】:

以上是关于Java 泛型与 C++ 模板有何不同?为啥我不能使用 int 作为参数?的主要内容,如果未能解决你的问题,请参考以下文章

JAVA 泛型与常见的数据结构和集合

C#泛型编程

学习笔记Java基础知识——泛型与集合

java 泛型实现原理

Java 泛型 lt;super T>中 super 怎么 理解?与 extends 有何不同

java 泛型与非泛型Java