Java 有泛型,C++ 提供了非常强大的编程模型 templates。那么,C++ 和 Java 泛型有什么区别呢?

有帮助吗?

解决方案

他们之间有很大的区别。在 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 的泛型有助于类型安全。

其他提示

Java 泛型是 大规模地 与 C++ 模板不同。

基本上,C++ 模板基本上是一个美化的预处理器/宏集(笔记: 因为有些人似乎无法理解类比,所以我并不是说模板处理是一个宏)。在 Java 中,它们基本上是语法糖,以最大限度地减少对象的样板转换。这是一个相当不错的 C++ 模板与 Java 泛型简介.

详细说明这一点:当您使用 C++ 模板时,您基本上是在创建代码的另一个副本,就像您使用 #define 宏。这允许你做一些事情,比如 int 模板定义中确定数组大小等的参数。

Java 不是这样工作的。在 Java 中,所有对象都从 java.lang.Object 因此,在泛型出现之前,您可以编写如下代码:

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 泛型的全部内容:用于铸造物体的包装器。那是因为 Java 泛型尚未完善。他们使用类型擦除。做出这个决定是因为 Java 泛型在本文中出现得太晚,以至于他们不想破坏向后兼容性(a Map<String, String> 每当 Map 被要求)。将此与不使用类型擦除的 .Net/C# 进行比较,这会导致各种差异(例如您可以使用原始类型和 IEnumerableIEnumerable<T> 彼此没有任何关系)。

使用 Java 5+ 编译器编译的泛型类可以在 JDK 1.4 上使用(假设它不使用任何其他需要 Java 5+ 的功能或类)。

这就是 Java 泛型被称为的原因 句法糖.

但是这个关于如何做仿制药的决定产生了深远的影响,以至于(非常棒的) Java 泛型常见问题解答 它的出现可以回答人们关于 Java 泛型的许多问题。

C++ 模板具有许多 Java 泛型所没有的功能:

  • 使用原始类型参数。

    例如:

    template<class T, int i>
    class Matrix {
      int T[i][i];
      ...
    }
    

    Java 不允许在泛型中使用原始类型参数。

  • 用于 默认类型参数, ,这是我在 Java 中怀念的一个特性,但这样做有向后兼容性的原因;

  • Java 允许对参数进行限制。

例如:

public class ObservableList<T extends List> {
  ...
}

确实需要强调的是,具有不同参数的模板调用实际上是不同的类型。他们甚至不共享静态成员。在 Java 中,情况并非如此。

除了与泛型的差异之外,为了完整起见,这里有一个 C++ 和 Java 的基本比较 (和 另一个).

我也可以建议 用Java思考. 。作为一名 C++ 程序员,许多概念(例如对象)已经成为第二天性,但存在细微的差异,因此即使您略读部分内容,也值得拥有介绍性文本。

学习 Java 时您将学到的很多内容都是库(包括标准库(JDK 中的库)和非标准库,其中包括 Spring 等常用库)。Java 语法比 C++ 语法更冗长,并且没有很多 C++ 功能(例如运算符重载、多重继承、析构函数机制等),但这也并不严格使其成为 C++ 的子集。

C++ 有模板。Java 有泛型,看起来有点像 C++ 模板,但它们非常非常不同。

顾名思义,模板的工作原理是为编译器提供一个(等等...)模板,编译器可以使用该模板通过填充模板参数来生成类型安全的代码。

据我了解,泛型的工作方式相反:编译器使用类型参数来验证使用它们的代码是否类型安全,但生成的结果代码根本没有类型。

将 C++ 模板视为 真的很好 宏系统和 Java 泛型作为自动生成类型转换的工具。

 

C++ 模板具有 Java 泛型所没有的另一个功能是专门化。这允许您对特定类型有不同的实现。例如,您可以拥有一个高度优化的版本 整数, ,同时仍然有其余类型的通用版本。或者您可以为指针和非指针类型使用不同的版本。如果您想在收到指针时对取消引用的对象进行操作,这会很方便。

这个主题有一个很好的解释 Java 泛型和集合作者:莫里斯·纳夫塔林、菲利普·瓦德勒。我强烈推荐这本书。去引用:

Java中的仿制物类似于C ++中的模板。...语法故意相似,语义是故意不同的。...从语义上讲,Java通用量是由擦除定义的,其中C ++模板是通过扩展定义的。

请阅读完整说明 这里.

alt text
(来源: 奥雷利网站)

基本上,据我所知,C++ 模板为每种类型创建代码副本,而 Java 泛型使用完全相同的代码。

是的,就是你 可以说 C++ 模板相当于 Java 泛型 概念 (尽管更正确的说法是 Java 泛型在概念上等同于 C++)

如果你熟悉C++的模板机制,你可能会认为泛型是相似的,但这种相似只是表面的。泛型不会为每个专业化生成一个新类,也不允许“模板元编程”。

从: Java 泛型

Java(和 C#)泛型似乎是一种简单的运行时类型替换机制。
C++ 模板是一种编译时构造,它为您提供了一种修改语言以满足您的需求的方法。它们实际上是编译器在编译期间执行的纯函数式语言。

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,将调用第一个方法,如果您调用 sumSpecial 对象,第三个将被调用。我认为这对于 Java 来说是不可能的。

我用一句话来概括:模板创建新类型,泛型限制现有类型。

@基思:

该代码实际上是错误的,除了较小的故障(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); }

下面的答案来自书本 破解编码面试 第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++ 模板还有许多其他差异。这些包括:

  • C++ 模板可以使用原始类型,例如 int。Java不能并且必须使用整数。
  • 在Java中,您可以将模板的类型参数限制为某种类型。例如,您可能会使用仿制药实现卡戴克,并指定类型参数必须从卡片游戏中延伸。
  • 在C ++中,可以实例化类型参数,而Java不支持此参数。
  • 在Java中,类型参数(即MyClass中的FOO)不能用于静态方法和变量,因为这些方法将在myclass和myclass之间共享。在 C++ 中,这些类是不同的,因此类型参数可用于静态方法和变量。
  • 在 Java 中,MyClass 的所有实例,无论其类型参数如何,都是相同的类型。类型参数在运行时被删除。在 C++ 中,具有不同类型参数的实例是不同的类型。

模板只不过是一个宏系统。语法糖。它们在实际编译之前完全展开(或者至少编译器的行为就像情况一样)。

例子:

假设我们想要两个函数。一个函数接受两个数字序列(列表、数组、向量等),并返回它们的内积。另一个函数接受一个长度,生成两个该长度的序列,将它们传递给第一个函数,然后返回其结果。问题是我们可能会在第二个函数中犯错误,因此这两个函数的长度实际上并不相同。在这种情况下,我们需要编译器警告我们。不是在程序运行时,而是在编译时。

在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++ 重写它,但它无法编译,抱怨模板的无限扩展。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top