Java接口和模板混淆

时间:2014-01-16 20:02:31

标签: java templates interface

我正在阅读一些关于某些不同编程语言中模板之间差异的问题。我理解这主要归功于这些问题: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代码更感兴趣。

2 个答案:

答案 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也不会。如果声明泛型参数必须扩展/实现某个接口,那么如果参数没有,则会出现编译时错误。