在java

时间:2016-11-01 21:00:56

标签: java c++ generics expression-templates

我试图看看模板表达模式是否可以在Java中模仿,以进行循环融合等优化。

作为示例,我将此表达式模板示例中的c ++类移植到java类:https://en.wikipedia.org/wiki/Expression_templates#Motivation_and_example

首先,表示向量表达式的模板类VecExpression<E>。它使用模板参数E并将类型E作为构造函数参数。然后,它会创建一个私有变量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();
    }
}

其次,扩展Vec的类VecExpression<Vec>Vec.class传递给超级构造函数,并实现{{1}中调用的get()size()方法}。class。

VecExpression<E>

第三,模板类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]; } } 扩展VecSum<E1, E2>,并使用其VecExpression<VecSum<E1, E2>方法返回两个get()的总和。该类型作为显式参数VecExpression<E>传递。

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();
    }
}

根据Louis Wasserman的评论编辑

但是,传递给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); } } 构造函数的类类型不起作用,因为表达式试图从参数化类型中获取类。路易斯指出,泛型类的实现不像在c ++中那样编译到不同的类。你会如何传递他们的类型,或者是否有另一种表达模板模式的方法?

1 个答案:

答案 0 :(得分:2)

您尝试做的事情不会在Java中工作,至少在您尝试通过使用Java泛型来进行编译时优化的情况下。原因是,与C ++模板不同,Java泛型在编译时不会得到解析。由于编译器在编译时没有解析类型,因此无法使用任何关于它的进行编译时优化。在某种意义上,Java编译器创建的字节代码是另一种方式&#34;擦除&#34;通用信息完全。如果您的Java类是class C<A>,则代码中出现A类型的所有类型,它将被类Object替换。如果您的Java类是class D<E extends F>,那么代码中出现E的所有地方都会被F替换。

在这种情况下,你可能会问为什么是泛型。答案是在编译器抛弃参数之前,它确实对输入进行了类型安全检查,并且它隐式地在方法返回上插入一个强制转换。这是几个版本中添加到Java的便利,但存在像ArrayList这样的Java容器类。只是你没有像现在一样拥有类型安全性,因为输入是明确的Object(即使你知道它应该放入任何对象,也可以放入任何对象)仅包含String个对象,并强制您将get的结果强制转换为String明确表示。

这与C ++模板形成对比,在C ++模板中,编译器从模板创建类定义并编译该类。然后可以将该类编译为任何其他类,包括可能使用特定于模板参数值的优化。此外,C ++中的模板特化允许更普遍的模板元编程,因为它允许您为模板参数中的递归创建基本案例。

(由于上面提到的原因,你不能拥有&#34;泛型专业化和#34;在Java的任何类似意义上 - Java编译器已经抛弃了泛型参数,所以你的&#34;专门的&#34;类 - 如果你试图定义这样的东西 - 将与&#34; generic&#34;类相同。)

最后,关于您的示例,请注意Class使用大写&#39; C&#39; Java中的类是任何其他类,包括它派生自Object。这不会让您了解C ++模板和Java泛型之间的编译时与运行时差异。