web-dev-qa-db-ja.com

Javaのジェネリックメソッドの型パラメーターとして型パラメーターを持つクラスを渡す

問題の概要:型パラメーター(たとえば、ArrayList<SomeClass>など)を持つクラスを型パラメーターとしてジェネリックメソッドに渡します。

メソッドがあるとしましょう:

    public static <T> T getGenericObjectFromJson(String json, Class<T> genericType){
        // details unimportant, basically returns an object of specified type
        return JsonParser.fromJson(json, genericType); 
    }

もちろん、このメソッドは、あらゆる種類のクラスで完璧に機能します。たとえば、次のようにメソッドを呼び出すことができます。

getGenericObjectFromJson(jsonString, User.class)

問題:私はこれができないことを発見しました:

getGenericObjectFromJson(jsonString, ArrayList<User>.class)

構文的には、これは明らかに無効です。しかし、私はこのようなことをどのように達成すればよいのか定かではありません。もちろん、ArrayList.classを渡すことはできますが、ジェネリック型を追加すると、構文的に有効ではなくなり、その方法を考えることはできません。

唯一の直接的な解決策は、次のようなものです(かなりおかしいようです):

getGenericObjectFromJson(jsonString, new ArrayList<User>().getClass())

ただし、とにかくジェネリック型は失われ、不明な型のArrayListが返されます(キャストは可能ですが)。さらに、不必要にオブジェクトをインスタンス化します。

これまでの私の唯一の解決策は、次のようにインスタンス化できるジェネリック型パラメーターを含むクラスでそのメソッドをラップすることでした:

public class JsonDeserializer<T>...

この場合、getGenericObjectFromJsonメソッドはクラスのジェネリック型を使用します。

The Question(s):最終的に、型パラメーターを使用してクラスを渡すことができない理由と、私がやろうとしたことを達成する方法があるかどうかに興味があります。

いつものように、この質問に何か問題があるかどうか教えてください。

52
Paul Richter

これは、実際にはJavaで「トリック」を使用して可能です。 C#狂信者からの圧力に屈しないでください! (j/k)

「トリック」は、ジェネリック型を拡張するクラスを作成し、.getGenericSuperclass()または.getGenericInterfaces()によって返されるTypeを介して親クラスの型パラメーターの値にアクセスすることです。 。

これは非常に面倒です。私たちの生活を簡素化するために、Googleはすでにコードの退屈な部分のほとんどを私たちのために書いており、それをGuavaを通して利用可能にしました。

TypeTokenクラスをチェックします。例えば:

TypeToken<List<String>> stringListTok = new TypeToken<List<String>>() {};

次に、TypeToken<T>の代わりにClass<T>を渡すと、それだけです。 Tで表される型を反映するメソッドを提供します。

これが内部的に行っているのは、単に.getClass().getGenericSuperclass()(または...Interfaces())を呼び出し、その後TypeからParameterizedTypeにsomeいキャストを行い、そこからすべての情報を取得することです( .getActualTypeArguments()など)。

最後に、Dependency Injectionで同様の処理を行いたい場合(つまり、クラスのコンストラクターにClass<T>を挿入する必要がある場合、またはインスタンスが必要なパラメーター化されたインターフェイスのインスタンスを取得する場合typeパラメーターに依存します)、Google Guice(GoogleのDIコンテナー)には、TypeLiteralと呼ばれる、問題を解決するための非常に類似したメカニズムがあります。舞台裏での使用とコードは、グアバのTypeTokenとほとんど同じです。ここで確認してください:TypeLiteral

59
Bruno Reis