2011-04-02 44 views
10

我知道相当多一点如何使用C++ - 模板 - 不是专家,介意你。与Java泛型(和斯卡拉,就此而言),我有我的diffuculties。也许,因为我试图将我的C++知识转化为Java世界。我在其他地方看过,“他们没有什么特别之处:Java泛型只是语法上的糖节省,C++模板只是一个荣耀的预处理器”:-)Java泛型,支持“专业化”?与C++模板的概念相似?

我很确定,两者都有点简化了。所以,了解大和细微的差别,我尝试启动专业化

C++我可以设计一个模板(类的功能),其作用于任何类型T支持我所需的操作:

template<typename T> 
T plus(T a, T b) { return a.add(b); } 

现在这潜在地增加了plus()操作的任何类型的可以add() [注1] [1]

。 0

因此,如果T支持add(T)我的模板工作。如果没有, 只要我不使用plus(),编译器就不会抱怨。在Python 我们称之为“鸭打字”:*如果它像鸭子,叫起来像鸭子, 它鸭子,这是修改*(当然,使用type_traits了一下, 但只要我们没有概念,这是如何C++模板工作,对吗?)

我想,这是怎么Java中的泛型工作,是不是?泛型类型I设备被用作“模板”,如何操作任何我尝试在其中放置的东西,对吧?据我的理解,我可以(或必须)将约束放在类型参数上:如果我想在我的模板中使用add,我必须声明类型参数为implement Addable。正确?所以,没有“鸭子打字”(好或坏)。现在

,在C++我可以选择上一类专门有没有add()

template<> 
T plus<MyX>(MyX a, MyX b) { return a + b; } 

即使所有其他类型仍然可以使用“默认”的实施,现在我添加了一个特殊的MyX - 没有运行时间开销。

是否有任何Java泛型具有相同目的的机制?当然,在编程中一切都是可行的,但我的意思是在概念上,没有任何技巧和魔法?

回答

8

不,Java中的泛型不能以这种方式工作。

对于泛型,如果没有泛型,就无法做任何事情 - 只需避免编写大量的强制类型,编译器确保所有类型都是类型安全的(只要您没有收到某些警告或者抑制那些)。

因此,每种类型的变量只能调用它的边界定义的方法(无鸭打字)。

此外,不存在代码生成(除了一些适配器方法委托给其他参数类型用于实现通用类型的目的方法)。假设你有这样的事情

/** 
* interface for objects who allow adding some other objects 
*/ 
interface Addable<T> { 
    /** returns the sum of this object and another object. */ 
    T plus(T summand); 
} 

然后,我们可以创造我们sum方法有两个参数:

public static <T extends Addable<T>> T sum(T first, T second) { 
    return first.plus(second); 
} 

静态方法被编译到相同的字节码这样的(在标注的其他类型的信息) :

public static Addable sum(Addable first, Addable second) { 
    return first.plus(second); 
} 

这就是所谓的类型擦除

现在这个方法可以被称为对于每对可加成类型的两个元件,像这样的:

public class Integer implements Addable<Integer> { 
    public Integer plus(Integer that) { 
     return new Integer(this.value + that.value); 
    } 

    // private implementation details omitted 
} 

什么这里发生的是,编译器会创建一个这样的附加的合成方法:

public Object plus(Object that) { 
    return this.plus((Integer)that); 
} 

这个方法只能通过具有正确类型的泛型代码来调用,这保证了编译器,假设你没有在某个地方进行一些不安全的转换 - 那么这里抛出的(Integer)会捕获错误(并引发ClassCastException)。

sum方法现在总是调用plus方法的第一个对象,没有办法解决这个问题。没有为每个类型参数生成的代码(这个是Java泛型和C++模板之间的关键区别),所以我们不能简单地用生成的方法替换一个生成的方法。

当然,可以创建第二sum方法等提出(超载)irreputable,但如果直接在源代码中使用MyX类型,而不是当您呼叫从sum方法,这将仅被选择这恰好其他一些通用的代码与MYX被参数,就像这样:

public static <T extends Addable<T>> product (int times, T factor) { 
    T result = factor; 
    while(n > 1) { 
     result = sum(result, factor); 
    } 
    return result; 
} 

现在product(5, new MyX(...))会调用我们sum(T,T)方法(这反过来又调用plus方法),没有任何重载sum(MyX, MyX)方法。

(JDK 7增加了一个新的dynamic方法分配模式,允许专门通过在运行时每次吵架,但这不是使用Java语言,只打算通过其他基于JVM的语言中使用。)

+2

类型擦除方法应该是'可加总和(可加第一,可加第二)' – irreputable 2011-04-02 13:10:32

+0

@irreputable:谢谢,你说得对。我改变了这一点。 – 2011-04-02 15:11:54

+0

对于'sum'泛型如何工作的很好的解释。现在我可以将它翻译成我以前的Java知识。但是这里擦除的是什么? “类型擦除”的名称是指什么? – towi 2011-04-03 14:11:43

2

HI,

java泛型与C++模板不同。

例子:

Java代码:

public <T> T sum(T a, T b) { 
    T newValue = a.sum(b); 
    return newValue; 
} 

在java中的代码不能工作,因为仿制药的基础是类java.lang.Object,所以你只能使用这个类的方法。

你可以构建这个Methis Hotel酒店是这样的:

public <T extends Number> T sum(T a, T b) { 
    T newValue = a.sum(b); 
    return newValue; 
} 
在这种情况下,仿制药的基础是类java.lang.Number中,所以你可以使用整数,双,龙ECC

..

方法“sum”依赖于java.lang.Number的实现。

再见

3

没有 - 但你的具体问题是更大的超载问题。

有没有问题定义2种plus方法,如这些

<T extends Addable> 
T plus(T a, T b) { .. } 

MyX plus(MyX a, MyX b) { .. } 

这个工程即使MyXAddable;的javac知道第二plus比1 plus更具体的,所以当你调用plus有两个MyX指定参数时,选择第二plus。在一定意义上的Java确实允许“专”版本的方法:

f(T1, T2, .. Tn) 

f(S1, S2, .. Sn) 

的伟大工程,如果每个SiTi

对于泛型类的子类型,我们可以做

class C<T extends Number> { ... } 

class C_Integer extends C<Integer>{ ... } 

调用者必须使用C_Integer而不是C<Integer>来选择“专业”版本。


关于鸭子打字:Java在静态打字中更加严格 - 除非它是鸭子,它不是鸭子。

+0

这只是重载,如果''加'方法是使用'T'从一些泛型方法调用并用'MyX'调用的,则它不起作用。 – 2011-04-02 14:00:30