2016-11-01 10 views
0

私は、テンプレートの表現パターンをJavaで模倣できるかどうか、ループの融合などの最適化を試みています。一例として、Javaで汎用クラス型を渡す

、IポートJavaクラスにこの式テンプレートの例で見つかったC++クラス:https://en.wikipedia.org/wiki/Expression_templates#Motivation_and_example

はまず、ベクトル表現を表すテンプレートクラスVecExpression<E>。テンプレートパラメータEを使用し、クラスタイプEをコンストラクタパラメータとして使用します。その後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.classを渡し、VecExpression<E>クラスで呼び出さget()size()メソッドを実装しVecExpression<Vec>を拡張するクラスVecのクラスタイプにthisキャストにプライベート変数thisAsEセットを作成します。

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]; 
    } 
} 

第三、VecExpression<VecSum<E1, E2>を拡張し、2つのVecExpression<E> Sの合計を返すために、そのget()方法を使用してテンプレートクラスVecSum<E1, E2>。型は明示的なパラメータ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(); 
    } 
} 

最後に、式テンプレートを使用して、メモリを1回通過する3つのベクトルを追加できるクラスを生成します。ルイ・ワッサーマンさんのコメント

あたりとして編集

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

式はパラメータ化された型からクラスを取得しようとしているのでしかし、VecSumコンストラクターに渡されるクラスの種類は動作しません。 Louisは、ジェネリッククラスの実装は、C++のように異なるクラスにコンパイルしないと指摘しました。どのように型を渡すか、あるいは式テンプレートパターンに別のアプローチがありますか?

+4

"SomeClass 、SomeClass など...別のクラスにコンパイルしないでください"いいえ、彼らはJavaではありません。 –

+0

"異なるクラスにコンパイルしてはいけませんか?"いいえ。 [type erasure](https://docs.oracle.com/javase/tutorial/java/generics/erasure.html)を参照してください – bradimus

+0

「タイプ消去」を検索してください:https://docs.oracle.com/javase/tutorial/ java/generics/erasure.html – Spoke44

答えて

2

あなたがやろうとしていることは、少なくともJavaジェネリックの使用によってコンパイル時の最適化を得るために使用しようとしている限り、Javaでは動作しません。その理由は、C++テンプレートとは異なり、Javaジェネリックはコンパイル時に解決されないからです。コンパイラはコンパイル時に型を解決しないので、コンパイル時の最適化に何も使用することはできません。ある意味では、Javaコンパイラによって作成されたバイトコードは、ジェネリック情報を完全に消去する別の方法になります。 Javaクラスがclass C<A>の場合は、コードにAタイプが表示されている場合は、クラスObjectに置き換えられます。 Javaクラスがclass D<E extends F>の場合、コードにEが表示されているところはすべてFに置き換えられます。

この場合、なぜジェネリックスが全然ないのか尋ねるかもしれません。その答えは、コンパイラがパラメータをスローする前に、入力に対してタイプセーフなチェックを行い、メソッドの返り値にキャストを暗黙的に挿入するというものです。これはJavaのいくつかのバージョンに追加された利便性ですが、ArrayListのようなJavaコンテナクラスが存在しました。入力が明示的にObjectであったのと同じ方法で型安全性を持たなかったことだけです。(例えば、Stringオブジェクトを含むと考えられていてもオブジェクトを入れて強制的にgetの結果を、たとえばStringに明示的にキャストすることができます)。

これは、コンパイラがテンプレートからクラス定義を作成し、そのクラスをコンパイルするC++テンプレートとは対照的です。そのクラスは、テンプレートパラメータの値に固有の最適化を潜在的に使用することを含め、他のクラスと同様にコンパイルできます。さらに、C++でのテンプレートの特殊化により、テンプレートのメタプログラミングがより一般的に可能になります。これは、テンプレートパラメータでの再帰の基本ケースを作成できるようにするためです。

(前述の理由から、Javaの類似の意味では「一般的な特殊化」はできません。Javaコンパイラは汎用パラメータを既に破棄していますので、あなたの「特殊な」クラス - -

最後に、あなたの例に関しては、ClassはJavaで大文字の 'C'を持つクラスは他のクラスと同じであることを覚えておいてください。これはObjectから派生しています。これは、コンパイル時とC++テンプレートとJavaジェネリック間のランタイムの違いを回避するものではありません。

+0

これは知っておきたいことです。ですから、Javaでメタプログラミングの最適化はリフレクションに限定されていますか? – AaronF

+0

私はリフレクションメタプログラミングを呼び出すことはありません。これは実行時にそれが起こるためです。これはセマンティクスかもしれません。 Javaでは、メタプログラミングという言葉は私が「メタプログラミング」という言葉を理解しているという意味ではありません。 – Brick

関連する問題