在 java 中传递通用 class 类型
Passing generic class type in java
我正在尝试看看是否可以在 Java 中模仿模板表达式模式,以进行循环融合等优化。
例如,我将这个表达式模板示例中的 c++ classes 移植到 java classes:https://en.wikipedia.org/wiki/Expression_templates#Motivation_and_example
首先,模板 class VecExpression<E>
表示向量表达式。它使用模板参数 E
并将 E
的 class 类型作为构造函数参数。然后它创建一个私有变量 thisAsE
设置为 this
转换为 E
的 class 类型
public abstract class VecExpression <E> {
private VecExpression thisAsE;
public VecExpression(Class<E> type) throws Exception {
if(type.isInstance(this)) {
thisAsE = (VecExpression)type.cast(this);
}
else {
throw new Exception("Class type must extend VecExpression");
}
}
public double get(int i) {
return thisAsE.get(i);
}
public int size() {
return thisAsE.size();
}
}
其次,class Vec
extending VecExpression<Vec>
将 Vec.class
传递给超级构造函数并实现 get()
和 size()
方法在 VecExpression<E>
class.
中调用
public class Vec extends VecExpression<Vec> {
private double[] elems;
public <E> Vec(VecExpression<E> expression) throws Exception {
super(Vec.class);
for(int i = 0; i < expression.size(); ++i) {
elems[i] = expression.get(i);
}
}
public Vec(double[] elems) throws Exception {
super(Vec.class);
this.elems = elems;
}
public double get(int i) {
return elems[i];
}
}
第三,模板 class VecSum<E1, E2>
扩展 VecExpression<VecSum<E1, E2>
,并使用其 get()
方法 return 两个 [=16] 的总和=]秒。类型作为显式参数传递 Class<VecSum<E1, E2>> type
.
public class VecSum <E1, E2> extends VecExpression<VecSum<E1, E2>> {
private VecExpression u;
private VecExpression v;
public VecSum(Class<VecSum<E1, E2>> type, VecExpression<E1> u, VecExpression<E2> v) throws Exception {
super(type);
if(u.size() != v.size()) {
throw new Exception("Vectors must be of the same size");
}
this.u = u;
this.v = v;
}
public double get(int i) {
return u.get(i) + v.get(i);
}
public int size() {
return v.size();
}
}
最后,我们使用表达式模板生成一个class,它可以通过一次内存添加三个向量。
public class Main {
public static void main(String[] args) throws Exception {
Vec a = new Vec(new double[] {1, 2, 3});
Vec b = new Vec(new double[] {1, 2, 3});
Vec c = new Vec(new double[] {1, 2, 3});
VecSum<Vec, Vec> ab = new VecSum<Vec, Vec>(VecSum<Vec, Vec>.class, a, b);
VecSum<VecSum<Vec, Vec>, Vec> abc = new VecSum<>(VecSum<VecSum<Vec, Vec>, Vec>.class, ab, c);
}
}
根据 Louis Wasserman 的评论编辑
但是,传递给 VecSum
构造函数的 class 类型不起作用,因为表达式试图从参数化类型中获取 class。 Louis 指出,泛型 class 的实现不会像在 C++ 中那样编译为不同的 classes。您将如何传递它们的类型,或者表达式模板模式是否有另一种方法?
您尝试做的事情在 Java 中不起作用,至少在您尝试通过使用 Java 来获得编译时优化的情况下是这样通用的。原因是,与 C++ 模板不同,Java 泛型不会在编译时解析。由于编译器在编译时没有解析类型,因此它不能使用任何关于它的东西来进行编译时优化。 Java 编译器创建的字节码,在某种意义上,完全是 "erasing" 通用信息的另一种方式。如果您的 Java class 是 class C<A>
,那么在您的代码中任何出现类型 A
的地方,它都会被 class Object
取代。如果你的 Java class 是 class D<E extends F>
那么你的代码中出现的任何地方 E
都会被替换为 F
.
在那种情况下,您可能会问为什么要使用泛型。答案是,在编译器丢弃参数之前,它确实对输入进行了类型安全检查,并且隐式地在方法 returns 上插入了一个强制转换。这是添加到 Java 几个版本之前的便利,但是像 ArrayList
这样的 Java 容器 class 已经存在。只是你没有像现在这样的类型安全,因为输入是明确的 Object
(让你放入任何对象,即使你知道它应该只包含,比如说,String
对象并强制您将 get
的结果显式转换为 String
。
这与 C++ 模板形成对比,在 C++ 模板中,编译器从模板创建 class 定义并编译该 class。然后可以将 class 编译为任何其他 class,包括可能使用特定于模板参数值的优化。此外,C++ 中的模板特化允许更普遍地进行模板元编程,因为它允许您在模板参数中创建递归的基本情况。
(出于上述原因,Java 中不能有任何类似意义的 "generic specialization" - Java 编译器已经丢弃了通用参数,因此您的 "specialized" class - 如果你试图定义这样的东西 - 将与 "generic" class 相同。)
最后,关于您的示例,请记住 Java 中的大写字母 'C' 的 Class
与任何其他示例一样是 class,包括它派生的来自 Object
。这不会让您了解 C++ 模板和 Java 泛型之间的编译时与运行时差异。
我正在尝试看看是否可以在 Java 中模仿模板表达式模式,以进行循环融合等优化。
例如,我将这个表达式模板示例中的 c++ classes 移植到 java classes:https://en.wikipedia.org/wiki/Expression_templates#Motivation_and_example
首先,模板 class VecExpression<E>
表示向量表达式。它使用模板参数 E
并将 E
的 class 类型作为构造函数参数。然后它创建一个私有变量 thisAsE
设置为 this
转换为 E
public abstract class VecExpression <E> {
private VecExpression thisAsE;
public VecExpression(Class<E> type) throws Exception {
if(type.isInstance(this)) {
thisAsE = (VecExpression)type.cast(this);
}
else {
throw new Exception("Class type must extend VecExpression");
}
}
public double get(int i) {
return thisAsE.get(i);
}
public int size() {
return thisAsE.size();
}
}
其次,class Vec
extending VecExpression<Vec>
将 Vec.class
传递给超级构造函数并实现 get()
和 size()
方法在 VecExpression<E>
class.
public class Vec extends VecExpression<Vec> {
private double[] elems;
public <E> Vec(VecExpression<E> expression) throws Exception {
super(Vec.class);
for(int i = 0; i < expression.size(); ++i) {
elems[i] = expression.get(i);
}
}
public Vec(double[] elems) throws Exception {
super(Vec.class);
this.elems = elems;
}
public double get(int i) {
return elems[i];
}
}
第三,模板 class VecSum<E1, E2>
扩展 VecExpression<VecSum<E1, E2>
,并使用其 get()
方法 return 两个 [=16] 的总和=]秒。类型作为显式参数传递 Class<VecSum<E1, E2>> type
.
public class VecSum <E1, E2> extends VecExpression<VecSum<E1, E2>> {
private VecExpression u;
private VecExpression v;
public VecSum(Class<VecSum<E1, E2>> type, VecExpression<E1> u, VecExpression<E2> v) throws Exception {
super(type);
if(u.size() != v.size()) {
throw new Exception("Vectors must be of the same size");
}
this.u = u;
this.v = v;
}
public double get(int i) {
return u.get(i) + v.get(i);
}
public int size() {
return v.size();
}
}
最后,我们使用表达式模板生成一个class,它可以通过一次内存添加三个向量。
public class Main {
public static void main(String[] args) throws Exception {
Vec a = new Vec(new double[] {1, 2, 3});
Vec b = new Vec(new double[] {1, 2, 3});
Vec c = new Vec(new double[] {1, 2, 3});
VecSum<Vec, Vec> ab = new VecSum<Vec, Vec>(VecSum<Vec, Vec>.class, a, b);
VecSum<VecSum<Vec, Vec>, Vec> abc = new VecSum<>(VecSum<VecSum<Vec, Vec>, Vec>.class, ab, c);
}
}
根据 Louis Wasserman 的评论编辑
但是,传递给 VecSum
构造函数的 class 类型不起作用,因为表达式试图从参数化类型中获取 class。 Louis 指出,泛型 class 的实现不会像在 C++ 中那样编译为不同的 classes。您将如何传递它们的类型,或者表达式模板模式是否有另一种方法?
您尝试做的事情在 Java 中不起作用,至少在您尝试通过使用 Java 来获得编译时优化的情况下是这样通用的。原因是,与 C++ 模板不同,Java 泛型不会在编译时解析。由于编译器在编译时没有解析类型,因此它不能使用任何关于它的东西来进行编译时优化。 Java 编译器创建的字节码,在某种意义上,完全是 "erasing" 通用信息的另一种方式。如果您的 Java class 是 class C<A>
,那么在您的代码中任何出现类型 A
的地方,它都会被 class Object
取代。如果你的 Java class 是 class D<E extends F>
那么你的代码中出现的任何地方 E
都会被替换为 F
.
在那种情况下,您可能会问为什么要使用泛型。答案是,在编译器丢弃参数之前,它确实对输入进行了类型安全检查,并且隐式地在方法 returns 上插入了一个强制转换。这是添加到 Java 几个版本之前的便利,但是像 ArrayList
这样的 Java 容器 class 已经存在。只是你没有像现在这样的类型安全,因为输入是明确的 Object
(让你放入任何对象,即使你知道它应该只包含,比如说,String
对象并强制您将 get
的结果显式转换为 String
。
这与 C++ 模板形成对比,在 C++ 模板中,编译器从模板创建 class 定义并编译该 class。然后可以将 class 编译为任何其他 class,包括可能使用特定于模板参数值的优化。此外,C++ 中的模板特化允许更普遍地进行模板元编程,因为它允许您在模板参数中创建递归的基本情况。
(出于上述原因,Java 中不能有任何类似意义的 "generic specialization" - Java 编译器已经丢弃了通用参数,因此您的 "specialized" class - 如果你试图定义这样的东西 - 将与 "generic" class 相同。)
最后,关于您的示例,请记住 Java 中的大写字母 'C' 的 Class
与任何其他示例一样是 class,包括它派生的来自 Object
。这不会让您了解 C++ 模板和 Java 泛型之间的编译时与运行时差异。