模板和泛型.为什么我可以在c++中做以下的事情,而不能在Java中做?我该如何克服呢?
Templates and Generics. Why can I do the following in C++ but not in Java? How do I overcome this?
考虑以下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++中,它们是两个独立的类(都是模板实例化的结果)。
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());
}
看看它是如何确保类型匹配的
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 为什么我不能在 FOR LOOP 中使用 i/10,C++?
- 为什么模板类中的对象不能返回值
- 为什么我不能在一个类的不同行中声明和定义成员变量?
- 为什么我不能在 C++ 中的特定函数重载中调用同一函数的任何其他重载?
- 为什么我不能将一个对象push_back到属于另一个类的对象向量中?
- 为什么我不能在 while 循环中创建线程?
- 为什么这个c++代码在Linux中可以正常工作,而在Windows中却不能
- 函数在C++中永远不能是抽象的吗
- 为什么我不能做*值++;将 1 递增到该内存位置中的值?
- 为什么在密封的ref类中属性不能是公共的
- 图形程序不能在borlandc++中运行.我能做什么呢?
- 为什么' int; '在C语言中可以很好地编译,但在c++中却不能
- 为什么我可以在C中隐式地将int字面量转换为int *,但在c++中却不能
- c++中pop_back()不能移除vector元素的问题
- 模板和泛型.为什么我可以在c++中做以下的事情,而不能在Java中做?我该如何克服呢?
- 在c++中似乎不能正确地向数组中添加对象
- 为什么我不能在函数中使用constexpr值,但我可以在这个值的作用域内做同样的事情
- C/C++ 中整数类型的最大值。为什么在这个例子中我不能正确计算它?
- 虚函数在c++中总是不能内联吗?