模板和泛型。为什么我可以在C ++中执行以下操作而不是在Java中执行以下操作我该如何克服这个问题?

时间:2015-07-20 10:13:45

标签: java c++ templates generics

考虑以下C ++程序:

#include <iostream>
using namespace std;

template<typename T>
class example
{
    public:
    void function (T a)
    {
        std::cout<<a.size ();
    }
};

int main() {
    example<string> a; // this doesn't
    string b = "a";
    //example<int> a; This gives an error
    a.function (b);
    // your code goes here
    return 0;
}

现在考虑以下Java程序:

import java.util.ArrayList;

class example<T> {

    public void function (T a)
    {
        System.out.println (a.toHexString(5)); /* this does not compile even when T is Integer */
    }


}

public class  Main
{
    public static void main (String[] args)
    {
        example<Integer> a = new example<Integer> (); 
        Integer b = 2;
        a.function(b);

        return;
    }
}

到目前为止,我主要是一名C ++开发人员,并且正在为工作目的学习Java。因此,来自使用模板的背景,仿制药让我很困惑。

来到我的问题:

在上面的C ++代码中,如果我将字符串作为模板参数传递,代码将编译并运行正常,因为字符串确实有size()方法。如果我使用int作为模板参数,我会发现错误,这是可以理解的。这里需要注意的是,如果我传递一个名为size()的方法的模板参数,C ++允许我编译并运行代码。

但是,在Java代码中,即使我传递Integer作为通用参数(?是一个术语?),它还有toHexString(int)方法,程序仍然无法编译。它返回一个错误:

cannot find symbol

这里的问题是什么?什么阻止我在Java中实现这种行为?

编辑:问题被标记为另一个问题可能重复: How do I call a method of a generic type object? 我会复制粘贴我的回答,为什么我认为这个问题有所不同。 以上问题可能是&#39;告诉我如何摆脱错误。我问的是什么阻止我在Java中实现上述效果?上述问题给了我这种疾病的药,而不是原因。

我在## java上提出了类似的问题并听说了一个新术语 - 具体化。我想知道它是否与此有关?

2 个答案:

答案 0 :(得分:2)

Java泛型通过 type erasure 实现。当你有这样的类签名时:

class example<T> { }

..该类被编译为常规Java类。为此,T有效地采用其上限的类型,在本例中为Object。如果您的示例中包含函数等方法,则参数类型为T

    public void function (T a)

...然后,在编译此函数时,这与参数类型为Object的几乎相同。因此,您无法在参数上调用toHexString等方法,因为该方法未在Object中定义。

另一方面,在C ++中,当模板被实例化而不是第一次编译时,会发生很多符号解析。这是关键的区别;在Java中,泛型类被编译为字节码,因此在编译泛型类时必须解析方法调用等(即,编译器必须能够决定方法来自哪个类或接口)。在C ++中,当编译器遇到模板时,它不会尝试解析引用或生成目标代码,除非并且直到模板被实例化。

考虑它的另一种方式:在Java中,example<String>example<Integer>都是通过同一个类实现的。在C ++中,它们将是两个独立的类(两者都来自模板的实例化)。

事实上,这就是为什么Java泛型类不是&#34;模板&#34;。在C ++中,类模板允许实例化类(即,它用作创建类的模板)。在Java中,泛型类允许参数化类型由单个类实现。

Java泛型类可以被认为非常类似于非泛型类,其中类型参数(例如T)被绑定类型替换(Object,除非另有说明) - 主要区别在于,当您在类的实例上调用方法时,编译器将执行其他类型检查(具有带类型参数的完整类型,以便T映射到其他类型),并且将有效地插入强制转换(这样你就可以调用一个返回T的方法,通过引用将T映射到某种类型,而不必转换返回类型。)

答案 1 :(得分:1)

问题在于Java泛型与C ++模板完全不同,并且从未被设计为如此。 Java泛型是使用一个特定目标设计的 - 在编译时添加强类型检查。因此,您将找到以下Java版本的近似值。

interface Hex {

    public String toHexString(int length);
}

class Example<T extends Hex> {

    public void function(T a) {
        System.out.println(a.toHexString(5));
    }

}

class StringWithHex implements Hex {

    @Override
    public String toHexString(int length) {
        return "Hex";
    }

}

public void test() {
    Example<StringWithHex> e = new Example<>();
    e.function(new StringWithHex());
}

了解它是如何确保类型匹配的。