tow*_*owi 11 c++ java generics template-specialization
我知道如何使用C++ - 模板 - 不是专家,请注意.使用Java Generics(和Scala,就此而言),我有我的困难.也许,因为我试图将我的C++知识转换为Java世界.我读到其他地方,"它们没什么相似之处:Java Generics只是语法糖节省演员,C++模板只是一个美化的预处理器":-)
我很确定,两者都是一个简化的视图.因此,为了理解大的和微妙的差异,我尝试从专业化开始:
在C++中,我可以设计一个模板(函数类),它作用于支持我所需操作的任何类型T:
template<typename T>
T plus(T a, T b) { return a.add(b); }
Run Code Online (Sandbox Code Playgroud)
现在这可能会将plus()操作添加到任何可以的类型add().[note1] [1]
因此,如果T支持add(T)我的模板woll工作.如果没有,只要我不使用,编译器就不会抱怨plus().在Python中我们称之为"鸭子打字":*如果它像鸭子一样嘎嘎叫,就像鸭子一样,它是一只鸭子.*(当然,使用type_traits这有点修改,但只要我们没有概念,这就是C++模板的工作方式,对吧?)
我想,那就是Java中的泛型如何工作,不是吗?通用类型I设备用作"模板"如何操作我试图放在那里的任何东西,对吧?据我所知,我可以(或必须?)对类型参数设置一些约束:如果我想add在我的模板中使用,我必须声明类型参数implement Addable.正确?所以,没有"鸭子打字"(无论好坏).
现在,在C++中,我可以选择专注于没有的类型add():
template<>
T plus<MyX>(MyX a, MyX b) { return a + b; }
Run Code Online (Sandbox Code Playgroud)
即使所有其他类型仍然可以使用"默认"实现,现在我添加了一个特殊的MyX- 没有运行时开销.
是否有任何Java Generics机制具有相同的目的?当然,在编程中,一切都是可行的,但我的意思是概念上,没有任何技巧和魔力?
不,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);
}
Run Code Online (Sandbox Code Playgroud)
然后我们可以sum用两个参数创建我们的方法:
public static <T extends Addable<T>> T sum(T first, T second) {
return first.plus(second);
}
Run Code Online (Sandbox Code Playgroud)
静态方法被编译为相同的字节码(注释中包含其他类型信息):
public static Addable sum(Addable first, Addable second) {
return first.plus(second);
}
Run Code Online (Sandbox Code Playgroud)
这称为类型擦除.
现在可以为可添加类型的每两对元素调用此方法,如下所示:
public class Integer implements Addable<Integer> {
public Integer plus(Integer that) {
return new Integer(this.value + that.value);
}
// private implementation details omitted
}
Run Code Online (Sandbox Code Playgroud)
这里发生的是编译器创建一个额外的合成方法,如下所示:
public Object plus(Object that) {
return this.plus((Integer)that);
}
Run Code Online (Sandbox Code Playgroud)
这个方法只能由具有正确类型的通用代码调用,这保证了编译器,假设你没有在某处做一些不安全的转换 - 那么(Integer)这里的转换会捕获错误(并抛出一个ClassCastException).
该sum方法现在总是调用plus第一个对象的方法,没有办法解决这个问题.没有为每个类型参数生成代码(这是 Java泛型和C++模板之间的关键区别),因此我们不能简单地用专门的方法替换其中一个生成的方法.
当然,您可以创建第二个sum方法,如无可争议的建议(带有重载),但只有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;
}
Run Code Online (Sandbox Code Playgroud)
现在product(5, new MyX(...))将调用我们的sum(T,T)方法(反过来调用该plus方法),而不是任何重载sum(MyX, MyX)方法.
(JDK 7添加了一种新的dynamic方法调度模式,它允许在运行时通过每个参数进行特化,但Java语言不会使用它,只能用于其他基于JVM的语言.)