2014-01-16 45 views
2

我正在阅读一些关于某些不同编程语言中模板之间差异的问题。我明白,主要感谢这个问题:What are the differences between Generics in C# and Java... and Templates in C++?。然而,当他开始谈论接口和添加内容时,我对接受的答案的结尾有点困惑。我主要从这个问题了解接口的概要:Explaining Interfaces to Students。问题中所陈述的内容我仍然感到困惑。因此,有人可以更好地解释这最后一部分:Java接口和模板混淆

正因为如此,C++编译器的地方您可以用模板做任何限制 - 基本上任何代码,你可以手工编写, 你可以得到的模板为你写。最明显的例子是 添加的东西:

在C#和Java泛型系统需要知道有哪些方法可以 一类,它需要通过这个下到虚拟机 。要告诉它的唯一方法是通过硬编码 实际类或使用接口。例如:

string addNames(T first,T second){return first.Name()+ second.Name(); }

该代码不会在C#或Java中编译,因为它不知道 类型T实际上提供了名为Name()的方法。你必须告诉 它 - 在C#中是这样的:

interface IHasName {string Name(); }; string addNames(T first,T second)其中T:IHasName {....}

然后你必须确保你传递的东西addNames 实现IHasName接口等等。 java的语法是 different(),但它遭受相同的 问题。

“经典”的情况下对这个问题是试图写一个函数 ,其执行此

串addNames(T第一,T秒){第二返回第一+; }

你实际上不能编写这段代码,因为没有办法用 声明一个带有+方法的接口。你失败了。

C++患有这些问题。编译器不关心 关于将类型传递给任何虚拟机 - 如果两个对象都有 .Name()函数,它将进行编译。如果他们不这样做,它不会。简单。

我真的很想理解这个答案中的代码,因为我很困惑如何.Name()方法在IHasName接口中工作。是否有人有一个更好的例子,可以进一步解释如何使用接口可以添加类名称到Person类或其他东西...

编辑:我更感兴趣的Java代码。

回答

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++患有这些问题。编译器不关心将类型传递给任何虚拟机 - 如果两个对象都有.Name()函数,它将编译。如果他们不这样做,它不会。简单。

Java也不会。如果声明泛型参数必须扩展/实现某个接口,那么在参数不存在的情况下,您将看到编译时错误。

+0

谢谢你的答案。我的问题不是代码是否工作,而是更多地寻找代码的解释,因为我相信这很好,但我不确定“如何”。看到像这样的完整程序更好地解释了这个例子,并且实际上回答了我的问题。谢谢! – LiverpoolFTW

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.util。*下的类,但很少有其他东西),Java不允许你达到这一点。你最好的拍摄是去一个有界类型通用就像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; 
    } 

    // ... 
} 

在这里,您可以限制通用的类型有一定超一流的,有做一些操作它。

+0

对不起,您好像在这里混淆了两种语言。 C++模板在编译时解析,而不是运行时。你的“巨大的宏观”是正确的,而你是自相矛盾的。你在想Java的泛型吗? – foo

+0

你是完全正确的,我忘记了更多关于C++的信息比我想象的更多)刚刚测试过它并将更新回答 –