模板和泛型.为什么我可以在c++中做以下的事情,而不能在Java中做?我该如何克服呢?

Templates and Generics. Why can I do the following in C++ but not in Java? How do I overcome this?

本文关键字:中做 不能 Java 何克服 克服 我可以 c++ 为什么 泛型      更新时间:2023-10-16

考虑以下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++代码中,如果我传递string作为模板参数,代码编译并运行良好,因为string确实有一个size()方法。如果我使用int作为模板形参,就会得到一个错误,这是可以理解的。这里需要注意的一点是,如果我传递一个模板形参,该形参有一个名为size()的方法,c++允许我编译并运行代码。

然而,在Java代码中,即使我传递Integer作为泛型参数(?是一个术语吗?)有toHexString(int)方法,程序仍然不能编译。它返回一个错误:

cannot find symbol

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

编辑:这个问题被标记为可能是另一个问题的重复:如何调用泛型类型对象的方法?我会复制粘贴我的回答,为什么我认为这个问题是不同的。上面的问题"潜在地"告诉我如何摆脱错误。我想问的是,是什么阻碍了我在Java中实现上述效果?这个问题给了我治病的药,而不是病因。

我在##java上提出了一个类似的问题,并听说了一个新的术语——具体化。我想知道这和这件事有没有关系?

Java泛型是通过类型擦除实现的。当你有这样的类签名时:

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泛型类不是"模板"。在c++中,类模板允许实例化类(也就是说,它作为创建类的模板)。在Java中,泛型类允许通过单个类实现参数化类型。 Java泛型类可以被认为与非泛型类非常相似,只是类型参数(如T)被绑定类型(Object,除非另有说明)所取代——主要区别在于,当你在类的实例上调用方法时,编译器将执行额外的类型检查(该类实例具有带类型参数的完整类型,因此T映射到其他类型)。并且将有效地插入强制转换(这样您就可以调用返回T的方法,通过引用将T映射到某种类型,而不必强制转换返回类型)。

问题是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());
}

看看它是如何确保类型匹配的