[英]Java Generics, support “Specialization”? Conceptual similarities to C++ Templates?
我知道如何使用C ++ - 模板 - 不是专家,请注意。 使用Java Generics (和Scala,就此而言),我有我的困难。 也许,因为我试图将我的C ++知识转换为Java世界。 我读到其他地方,“它们没什么相似之处:Java Generics只是语法糖节省演员,C ++模板只是一个美化的预处理器”:-)
我很确定,两者都是一个简化的视图。 因此,为了理解大的和微妙的差异,我尝试从专业化开始:
在C ++中,我可以设计一个模板 (函数类),它作用于支持我所需操作的任何类型T
:
template<typename T>
T plus(T a, T b) { return a.add(b); }
这现在可能会将plus()
操作add()
到任何可以add()
类型。[note1] [1]
因此,如果T
支持add(T)
我的模板woll工作。 如果没有,只要我不使用plus()
,编译器就不会抱怨。 在Python中我们称之为“鸭子打字” :*如果它像鸭子一样嘎嘎叫,就像鸭子一样,它是一只鸭子。*(当然,使用type_traits这有点修改,但只要我们没有概念,这就是C ++模板的工作方式,对吧?)
我想,那就是Java中的泛型如何工作,不是吗? 通用类型I设备用作“模板”如何操作我试图放在那里的任何东西,对吧? 据我所知,我可以(或必须?)对类型参数设置一些约束 :如果我想在模板中使用add
,我必须声明type参数来implement Addable
。 正确? 所以,没有“鸭子打字”(无论好坏)。
现在,在C ++中,我可以选择专注于没有add()
的类型:
template<>
T plus<MyX>(MyX a, MyX b) { return a + b; }
即使所有其他类型仍然可以使用“默认”实现,现在我为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);
}
然后我们可以使用两个参数创建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
方法,如无可争议的建议(带有重载),但只有在源代码中直接使用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 plus
方法没有问题
<T extends Addable>
T plus(T a, T b) { .. }
MyX plus(MyX a, MyX b) { .. }
即使MyX
是Addable
,这也能正常工作; javac知道第二个plus
比第一个plus
号更具体,所以当你用两个MyX
args调用plus
时,选择第二个plus
。 从某种意义上说,Java确实允许“专用”版本的方法:
f(T1, T2, .. Tn)
f(S1, S2, .. Sn)
如果每个Si
是Ti
的子类型,则效果很好
对于泛型类,我们可以做到
class C<T extends Number> { ... }
class C_Integer extends C<Integer>{ ... }
调用者必须使用C_Integer
而不是C<Integer>
来选择“专用”版本。
关于鸭子打字:Java在静态打字中更严格 - 除非它是鸭子,它不是鸭子。
HI,
java Generics它与C ++模板不同。
例:
Java代码:
public <T> T sum(T a, T b) {
T newValue = a.sum(b);
return newValue;
}
在java中,此代码不起作用,因为泛型基类是类java.lang.Object,因此您只能使用此类的方法。
你可以像这样构建这个methis:
public <T extends Number> T sum(T a, T b) {
T newValue = a.sum(b);
return newValue;
}
在这种情况下,泛型的基础是java.lang.Number类,所以你可以使用Integer,Double,Long ecc ..
方法“sum”取决于java.lang.Number的实现。
再见
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.