我正在阅读一些关于某些不同编程语言中模板之间差异的问题。我理解这主要归功于这些问题:What are the differences between Generics in C# and Java... and Templates in C++?。然而,当他开始谈论接口和添加内容时,我对接受的答案的结尾感到有些困惑。我理解接口的一般主要来自这个问题:Explaining Interfaces to Students。我仍然对问题中所说的内容感到困惑。那么有人可以更好地解释这最后一部分:
因此,C ++编译器对模板的功能没有任何限制 - 基本上你可以手动编写任何代码, 你可以得到模板给你写。最明显的例子是 添加东西:
在C#和Java中,泛型系统需要知道哪些方法 可用于一个类,它需要将其传递给虚拟 机。告诉它的唯一方法是通过硬编码 实际的类,或使用接口。例如:
string addNames(T first,T second){return first.Name()+ 第二个名字(); }
该代码不会在C#或Java中编译,因为它不知道 类型T实际上提供了一个名为Name()的方法。你必须告诉 它 - 在C#中像这样:
interface IHasName {string Name(); }; string addNames(T first,T 第二)其中T:IHasName {....}
然后你必须确保你传递给addNames的东西 实现IHasName接口等。 java语法是 不同(),但它也受到同样的影响 问题。
此问题的“经典”案例是尝试编写函数 这样做
string addNames(T first,T second){return first + second; }
您实际上无法编写此代码,因为没有办法 声明一个带有+方法的接口。你失败了。
C ++没有遇到这些问题。编译器不关心 将类型传递给任何VM - 如果你的对象都有 .Name()函数,它会编译。如果他们不这样做,那就不会。简单。
我真的很想理解这个答案中的代码,因为我很困惑.Name()方法在IHasName接口中是如何工作的。有人有一个更好的例子,可以进一步解释如何使用接口向Person类或其他东西添加类似名称的内容......
编辑:我对Java代码更感兴趣。答案 0 :(得分:2)
'+'在C ++中编译的原因是C ++模板(和C ++专家请原谅我的笨拙)就像巨大的宏一样。它会等到模板被实例化以检查每个操作是否可行(仍然在编译时,因为foo在他们的评论中警告我)。
例如这段代码:
#include "stdafx.h"
#include <stdio.h>
#include <vector>
#include <iostream>
template <class T> class adder {
public:
adder();
T add(T t1, T t2);
};
template <class T> adder<T>::adder() {}
template <class T> T adder<T>::add (T t1, T t2) {
return t1 + t2;
}
using namespace std;
typedef vector<int> int_vector;
int _tmain(int argc, _TCHAR* argv[])
{
adder<int_vector> vector_adder;
int_vector v1;
int_vector v2;
v1.push_back(1);
v1.push_back(2);
v2.push_back(3);
v2.push_back(4);
v2.push_back(5);
// will fail here, in compile time!
int_vector v3 = vector_adder.add(v1, v2);
for (int_vector::iterator it = v3.begin(); it != v3.end(); it++) {
cout << *it;
}
return 0;
}
在我尝试添加两个向量之前,它会完美运行;在那一刻,C ++编译器将意识到我在模板中使用的实际类型没有重载。但是,我本可以在模板中定义一些其他操作,如果我没有尝试添加实际类型,编译器就没有意识到(然后它会尝试解决宏)。但是adder<int>
之类的东西会完美起作用。
Java不允许你到达那一点,因为它不允许使用泛型类型进行实际操作(定义泛型包含如java.util。*中的类,但很少其他)。你最好的方法就是在java文档中使用有界类型泛型:
public class NaturalNumber<T extends Integer> {
private T n;
public NaturalNumber(T n) { this.n = n; }
public boolean isEven() {
return n.intValue() % 2 == 0;
}
// ...
}
在这里,您可以将泛型的类型限制为某个超类,并对其进行一些操作。
答案 1 :(得分:1)
C ++模板非常不同。但我不明白为什么Java的泛型不能做你在那里引用的东西。
interface IHasName {
String getName();
}
class Person implements IHasName {
private final String name;
public Person(String name) {
this.name = name;
}
@Override
public String getName() {
return name;
}
}
class GenericUtility {
public static <T extends IHasName> String addNames(T first, T second) {
return first.getName() + ", " + second.getName();
}
}
class Main {
public static void main(String[] args) {
Person first = new Person("Peter");
IHasName second = new IHasName() {
@Override
public String getName() {
return "John";
}
};
String result = GenericUtility.addNames(first, second);
System.out.println(result);
}
}
打印
Peter, John
我会认为这是按预期工作的。
您实际上无法编写此代码,因为无法使用+方法声明接口。你失败了。
Java不允许根据参数类型覆盖+
运算符。这里没有失败,因为你必须首先采取不同的方法(可以写成接口)。
C ++没有遇到这些问题。编译器不关心将类型传递给任何VM - 如果你的对象都有.Name()函数,它将编译。如果他们不这样做,那就不会。简单。
Java也不会。如果声明泛型参数必须扩展/实现某个接口,那么如果参数没有,则会出现编译时错误。