考虑以下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上提出了类似的问题并听说了一个新术语 - 具体化。我想知道它是否与此有关?
答案 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());
}
了解它是如何确保类型匹配的。