Javaでジェネリック型のインスタンスを作成しますか?


576

Javaでジェネリック型のインスタンスを作成することは可能ですか?私が見たものに基づいて考えています。答えはno型消去のため)である、誰かが私が欠けているものを見ることができるかどうか興味があります:

class SomeContainer<E>
{
    E createContents()
    {
        return what???
    }
}

編集:私の問題を解決するためにスーパータイプトークンを使用できることがわかりましたが、以下の回答のいくつかが示しているように、多くのリフレクションベースのコードが必要です。

Ian RobertsonのArtima Articleとは劇的に異なるものを誰かが思い付くかどうかを確認するために、これを少しの間開いたままにします。


2
Androidデバイスでパフォーマンスをテストしたところです。10000オペレーションと:8-9 msは新しいSomeClass()を取り、9-11 msはFactory <SomeClass> .createInstance()を取り、64-71 msは最短のリフレクションを取ります:SomeClass z = SomeClass.class.newInstance()。そして、すべてのテストは単一のtry-catchブロックで行われました。リフレクションnewInstance()は4つの異なる例外をスローします、覚えていますか?そこで、ファクトリーパターンを使用することにしました
Deepscorn


4
Java 8では、コンストラクター参照またはラムダを渡すことができるため、この問題を回避するのは簡単です。詳細については、以下の私の回答を参照してください。
Daniel Pryden 2016年

これは、そのようなコードを書くのは悪い考えだと思います。根本的な問題を解決するためのよりエレガントで読みやすい方法です。
Krzysztof Cichocki 2017年

1
@DavidCitronは「しばらくの間」彼は言った...それから11年になります...
MC皇帝

回答:


332

あなたは正しいです。あなたはできませんnew E()。しかし、あなたはそれを

private static class SomeContainer<E> {
    E createContents(Class<E> clazz) {
        return clazz.newInstance();
    }
}

それは苦痛です。しかし、それは機能します。ファクトリーパターンでそれをラップすると、もう少し許容範囲が広くなります。


11
ええ、私はその解決策を見ましたが、インスタンス化するタイプのClassオブジェクトへの参照がすでにある場合にのみ機能します。
David Citron

11
そうだね。E.classを実行できればいいのですが、消去のためにObject.classを提供するだけです:)
Justin Rudd

6
これがこの問題に対する正しいアプローチです。それは通常あなたが望むものではありませんが、あなたが得るものです。
Joachim Sauer

38
そして、どのようにcreateContents()メソッドを呼び出しますか?
Alexis Dufrenoy

8
これはもはやこれを行う唯一の方法ではありませんClass<?>。GuavaとTypeTokenを使用して参照を渡す必要がないより良い方法があります。コードとリンクについてはこの回答を参照してください!

129

Dunnoが役立つ場合がありますが、ジェネリック型を(匿名で含む)サブクラス化すると、型情報はリフレクションを介して利用できます。例えば、

public abstract class Foo<E> {

  public E instance;  

  public Foo() throws Exception {
    instance = ((Class)((ParameterizedType)this.getClass().
       getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
    ...
  }

}

したがって、Fooをサブクラス化すると、Barのインスタンスが取得されます。たとえば、

// notice that this in anonymous subclass of Foo
assert( new Foo<Bar>() {}.instance instanceof Bar );

しかし、それは多くの作業であり、サブクラスに対してのみ機能します。でも重宝します。


2
はい、これは特にジェネリッククラスが抽象である場合に便利です。具体的なサブクラスでこれを行うことができます:)
Pierre Henry

このメソッドは、クラスFooが抽象でない場合にも機能します。しかし、なぜFooの匿名サブクラスでしか機能しないのですか?Foo具体的に(私たちはを省略abstract)作成するとしますが、なぜnew Foo<Bar>();エラーにnew Foo<Bar>(){};ならないのですか?(例外:「クラスをParameterizedTypeにキャストできません」)
Tim Kuipers

2
@TimKuipers <E>in class Foo<E>は特定の型にバインドされていません。あなたはいつでも異例の行動が表示されますEされていない静的:のように、拘束をnew Foo<Bar>()new Foo<T>() {...}またはclass Fizz <E> extends Foo<E>。最初のケースは静的にバインドされておらず、コンパイル時に消去されます。2番目のケースは、代わりに別の型変数(T)を代入しますEが、まだバインドされていません。そして最後のケースでは、それEがまだ拘束されていないことは明らかです。
ウィリアム価格

4
型パラメーターを静的にバインドする例は次のとおりclass Fizz extends Foo<Bar>です。この場合、ユーザーはFizzであり、以外では取得Foo<Bar>できないものを取得しFoo<Bar>ます。したがって、この場合、コンパイラーはその情報をクラスメタデータにエンコードして、リフレクションコードFizzとして利用できるようParameterizedTypeにします。new Foo<Bar>() {...}同じように匿名の内部クラスを作成するとFizz、コンパイラーの代わりに、外部クラスがコンパイルされるまでわからない「匿名」のクラス名が生成されます。
ウィリアム価格

4
これは、型引数もParameterizedTypeである場合は機能しないことに注意してください。たとえば、Foo<Bar<Baz>>ParameterizedTypeImpl明示的に作成できないインスタンスを作成します。したがって、getActualTypeArguments()[0]がを返すかどうかを確認することをお勧めしParameterizedTypeます。もしそうなら、あなたは生の型を取得し、代わりにそのインスタンスを作成したいでしょう。
クラッシュ

106

Java 8では、Supplier機能インターフェースを使用してこれをかなり簡単に実現できます。

class SomeContainer<E> {
  private Supplier<E> supplier;

  SomeContainer(Supplier<E> supplier) {
    this.supplier = supplier;
  }

  E createContents() {
    return supplier.get();
  }
}

このクラスは次のように作成します。

SomeContainer<String> stringContainer = new SomeContainer<>(String::new);

String::newその行の構文はコンストラクタ参照です。

コンストラクタが引数を取る場合は、代わりにラムダ式を使用できます。

SomeContainer<BigInteger> bigIntegerContainer
    = new SomeContainer<>(() -> new BigInteger(1));

6
いいね。リフレクションを回避し、例外を扱う必要があります。
Bruno Leite 2017年

2
いいね Androidユーザーにとっては残念ながら、これにはAPIレベル24以上が必要です。
マイケルアップダイク2018年

1
それほど手間のかからない機能的なアプローチによると、これは私の意見では受け入れられる答えです
Tomas

2
…そして、それよりも後ろの技術パターンがJavaのラムダ式とメソッド参照のサポートよりも古いことを示すこのさらに古い回答と違いはありませんが、コンパイラをアップグレードすると、古いコードを使用できます…
Holger

ちょうど置くことが可能でしょうSomeContainer stringContainer = new SomeContainer(String::new);か?
アーロンフランケ

87

あなたは降圧金を渡すために、何らかの種類の抽象的なファクトリーが必要です:

interface Factory<E> {
    E create();
}

class SomeContainer<E> {
    private final Factory<E> factory;
    SomeContainer(Factory<E> factory) {
        this.factory = factory;
    }
    E createContents() {
        return factory.create();
    }
}

..そしてFactory.create()はどのように見えますか?
OhadR 2016年

6
@OhadR Factory<>はインターフェースであるため、本体はありません。ポイントは、インスタンスを構築するために必要なコードを「知っている」メソッドにバックを渡すために、間接参照のレイヤーが必要であることです。これをメタ言語的ではなく通常のコードで行う方が良いです。ClassまたはConstructor、リフレクションが全体の世界を傷つけるからです。
トムホーティン-タックライン2016年

2
:今日あなたは、このようなメソッド参照式で工場出荷時のインスタンスを作成することができますSomeContainer<SomeElement> cont = new SomeContainer<>(SomeElement::new);
LII

24
package org.foo.com;

import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;

/**
 * Basically the same answer as noah's.
 */
public class Home<E>
{

    @SuppressWarnings ("unchecked")
    public Class<E> getTypeParameterClass()
    {
        Type type = getClass().getGenericSuperclass();
        ParameterizedType paramType = (ParameterizedType) type;
        return (Class<E>) paramType.getActualTypeArguments()[0];
    }

    private static class StringHome extends Home<String>
    {
    }

    private static class StringBuilderHome extends Home<StringBuilder>
    {
    }

    private static class StringBufferHome extends Home<StringBuffer>
    {
    }   

    /**
     * This prints "String", "StringBuilder" and "StringBuffer"
     */
    public static void main(String[] args) throws InstantiationException, IllegalAccessException
    {
        Object object0 = new StringHome().getTypeParameterClass().newInstance();
        Object object1 = new StringBuilderHome().getTypeParameterClass().newInstance();
        Object object2 = new StringBufferHome().getTypeParameterClass().newInstance();
        System.out.println(object0.getClass().getSimpleName());
        System.out.println(object1.getClass().getSimpleName());
        System.out.println(object2.getClass().getSimpleName());
    }

}

3
このコードによる適切なアプローチでは、ジェネリック型をジェネリック型で使用すると、ClassCastExceptionが発生する可能性があります。次に、actualType引数を取得します。それがParamterizedTypeでもあることを確認し、そうであれば、RawType(またはこれよりも良いもの)を返します。これに関するもう1つの問題は、このコードがClassCastExeptionをスローすると、さらに拡張する場合です。
DamianLeszczyński-Vash

3
原因:java.lang.ClassCastException:sun.reflect.generics.reflectiveObjects.ParameterizedTypeImplをjava.lang.Classにキャストできない
juan

@DamianLeszczyński-Vashも失敗します。例class GenericHome<T> extends Home<T>{}
Holger

22

ジェネリッククラス内に型引数の新しいインスタンスが必要な場合は、コンストラクターにそのクラスを要求させます...

public final class Foo<T> {

    private Class<T> typeArgumentClass;

    public Foo(Class<T> typeArgumentClass) {

        this.typeArgumentClass = typeArgumentClass;
    }

    public void doSomethingThatRequiresNewT() throws Exception {

        T myNewT = typeArgumentClass.newInstance();
        ...
    }
}

使用法:

Foo<Bar> barFoo = new Foo<Bar>(Bar.class);
Foo<Etc> etcFoo = new Foo<Etc>(Etc.class);

長所:

  • Robertsonのスーパータイプトークン(STT)アプローチよりもはるかに単純(かつ問題が少ない)。
  • STTアプローチ(携帯電話を朝食に食べます)よりもはるかに効率的です。

短所:

  • Classをデフォルトのコンストラクターに渡すことはできません(これがFooがfinalである理由です)。デフォルトのコンストラクタが本当に必要な場合は、いつでもセッターメソッドを追加できますが、後で彼女に呼び出しを与えることを忘れないでください。
  • Robertsonの反対意見...黒い羊よりもバーの数が多い(ただし、型引数クラスをもう一度指定しても正確には殺されない)。ロバートソンの主張に反して、コンパイラは型の正確さを保証するので、これはとにかくDRYプリンシパルに違反しません。
  • 完全にFoo<L>証明されているわけではありません。はじめに...newInstance()型引数クラスは、デフォルトコンストラクタを持っていない場合ワブラーをスローします。とにかく、これはすべての既知のソリューションに当てはまります。
  • STTアプローチの完全なカプセル化が欠如しています。しかし、大したことではありません(STTの法外なパフォーマンスオーバーヘッドを考えると)。

22

これを今すぐ行うことができ、一連のリフレクションコードは必要ありません。

import com.google.common.reflect.TypeToken;

public class Q26289147
{
    public static void main(final String[] args) throws IllegalAccessException, InstantiationException
    {
        final StrawManParameterizedClass<String> smpc = new StrawManParameterizedClass<String>() {};
        final String string = (String) smpc.type.getRawType().newInstance();
        System.out.format("string = \"%s\"",string);
    }

    static abstract class StrawManParameterizedClass<T>
    {
        final TypeToken<T> type = new TypeToken<T>(getClass()) {};
    }
}

もちろん、リフレクションを必要とするコンストラクタを呼び出す必要があるが、それが十分に文書化されている場合、このトリックはそうではありません!

TypeTokenJavaDocは次のとおりです。


6
このソリューションは、@ noahのリフレクションを使用した回答と同様に、限られたケースで機能します。今日はすべて試しました...そして、パラメータークラスのインスタンスをパラメーター化されたクラスに渡すことで終了しました(.newInstance()を呼び出せるようにするため)。「ジェネリック」の非常に大きな欠陥...新しいFoo <Bar>(Bar.class); ... class Foo <T> {private final Class <T> mTFactory; Foo(Class <T> tClass){mTFactory = tClass; ...} Tインスタンス= tFactory.newInstance(); }
yvolk 2015年

これはすべてのケースで機能します。一般的なパラメーターを取る静的なファクトリメソッドも含まれます

13

より機能的なアプローチについて考えてください。何もないところからEを作成するのではなく(明らかにコードのにおいです)、作成方法を知っている関数を渡します。

E createContents(Callable<E> makeone) {
     return makeone.call(); // most simple case clearly not that useful
}

6
技術的には、関数を渡すのではなく、関数オブジェクトファンクタとも呼ばれます)を渡します。
Lorne Laliberte、2015

3
または、代わりにException使用Supplier<E>をキャッチすることを克服するために。
マーチンD

10

Javaチュートリアルから-ジェネリクスの制限

タイプパラメータのインスタンスを作成できません

タイプパラメータのインスタンスを作成することはできません。たとえば、次のコードはコンパイル時エラーを引き起こします。

public static <E> void append(List<E> list) {
    E elem = new E();  // compile-time error
    list.add(elem);
}

回避策として、リフレクションを通じてタイプパラメータのオブジェクトを作成できます。

public static <E> void append(List<E> list, Class<E> cls) throws Exception {
    E elem = cls.newInstance();   // OK
    list.add(elem);
}

次のようにして、appendメソッドを呼び出すことができます。

List<String> ls = new ArrayList<>();
append(ls, String.class);

6

ここに私が思いついたオプションがあります、それは助けるかもしれません:

public static class Container<E> {
    private Class<E> clazz;

    public Container(Class<E> clazz) {
        this.clazz = clazz;
    }

    public E createContents() throws Exception {
        return clazz.newInstance();
    }
}

編集:または、このコンストラクタを使用できます(ただし、Eのインスタンスが必要です)。

@SuppressWarnings("unchecked")
public Container(E instance) {
    this.clazz = (Class<E>) instance.getClass();
}

ええ、これはジェネリックスがなくても同じように機能します。ジェネリックスを使用すると、このコンテナーのインスタンス化は少し冗長になります( "E"を2倍に指定する必要があります)。
David Citron

まあ、それはあなたがJavaとジェネリックスを使うときに起こることです...それらはきれいではなく、厳しい制限があります...
Mike Stone

6

次のように、インスタンス化中にクラス名を2回入力したくない場合:

new SomeContainer<SomeType>(SomeType.class);

ファクトリメソッドを使用できます。

<E> SomeContainer<E> createContainer(Class<E> class); 

のように:

public class Container<E> {

    public static <E> Container<E> create(Class<E> c) {
        return new Container<E>(c);
    }

    Class<E> c;

    public Container(Class<E> c) {
        super();
        this.c = c;
    }

    public E createInstance()
            throws InstantiationException,
            IllegalAccessException {
        return c.newInstance();
    }

}

6

残念ながらJavaはあなたがやりたいことを許可しません。公式の回避策を参照してください

タイプパラメータのインスタンスを作成することはできません。たとえば、次のコードはコンパイル時エラーを引き起こします。

public static <E> void append(List<E> list) {
    E elem = new E();  // compile-time error
    list.add(elem);
}

回避策として、リフレクションを通じてタイプパラメータのオブジェクトを作成できます。

public static <E> void append(List<E> list, Class<E> cls) throws Exception {
    E elem = cls.newInstance();   // OK
    list.add(elem);
}

次のようにして、appendメソッドを呼び出すことができます。

List<String> ls = new ArrayList<>();
append(ls, String.class);

投票する理由を教えてください。公式の回避策が悪い解決策である理由がわかりません。ありがとう。
Neepsnikeep 2017

あなたの答えは基本的にジャスティン・ラッドのものと同じなので、私はあなたが票を落とすと思います:stackoverflow.com/a/75254/103412
Torsten

5

コンパイル時にEを操作する場合、実際のジェネリック型 "E"は特に気にしない(リフレクションを使用するか、ジェネリック型の基本クラスを操作する)ため、サブクラスにEのインスタンスを提供させます。

Abstract class SomeContainer<E>
{

    abstract protected  E createContents();
    public doWork(){
        E obj = createContents();
        // Do the work with E 

     }
}


BlackContainer extends SomeContainer<Black>{
    Black createContents() {
        return new  Black();
    }
}

4

以下を使用できます。

Class.forName(String).getConstructor(arguments types).newInstance(arguments)

ただし、パッケージを含む正確なクラス名を指定する必要があります。java.io.FileInputStream。これを使って数式パーサーを作成しました。


15
そして、実行時にジェネリック型の正確なクラス名をどのように取得しますか?
David Citron

そのクラスのインスタンスを使用して保存する必要があります。できるが、ほとんど便利ではない。ジェネリックにタイプE(またはTまたはその他)のメンバーがあった場合、そのバイナリ名の取得は単にfoo.getClass().getName()です。そのインスタンスはどこから来たのですか?私は現在、現在取り組んでいるプロジェクトのコンストラクタに1つを渡しています。
Mark Storer

3

これが手助けするのに遅すぎないことを願っています!!!

Javaはタイプセーフであり、オブジェクトのみがインスタンスを作成できます。

私の場合、パラメーターをcreateContentsメソッドに渡すことができません。私の解決策は、すべての怒鳴る答えの代わりに拡張を使用しています。

private static class SomeContainer<E extends Object> {
    E e;
    E createContents() throws Exception{
        return (E) e.getClass().getDeclaredConstructor().newInstance();
    }
}

これは、パラメーターを渡すことができない例の例です。

public class SomeContainer<E extends Object> {
    E object;

    void resetObject throws Exception{
        object = (E) object.getClass().getDeclaredConstructor().newInstance();
    }
}

オブジェクトタイプなしでジェネリッククラスを拡張する場合、リフレクション作成ランタイムエラーを使用します。ジェネリック型をオブジェクトに拡張するには、このエラーをコンパイル時エラーに変換します。



2

できると思ったのですが、かなりがっかりしました。うまくいきませんが、共有する価値はあります。

多分誰かが修正できます:

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

interface SomeContainer<E> {
    E createContents();
}

public class Main {

    @SuppressWarnings("unchecked")
    public static <E> SomeContainer<E> createSomeContainer() {
        return (SomeContainer<E>) Proxy.newProxyInstance(Main.class.getClassLoader(),
                new Class[]{ SomeContainer.class }, new InvocationHandler() {
            @Override
            public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
                Class<?> returnType = method.getReturnType();
                return returnType.newInstance();
            }
        });
    }

    public static void main(String[] args) {
        SomeContainer<String> container = createSomeContainer();

    [*] System.out.println("String created: [" +container.createContents()+"]");

    }
}

それは生成します:

Exception in thread "main" java.lang.ClassCastException: java.lang.Object cannot be cast to java.lang.String
    at Main.main(Main.java:26)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:601)
    at com.intellij.rt.execution.application.AppMain.main(AppMain.java:120)

26行目は[*]

唯一の実行可能なソリューションは、@ JustinRuddによるソリューションです。


2

@ノアの答えの改善。

変更理由

a]順序を変更した場合に備えて、1つ以上のジェネリック型を使用する方が安全です。

b]クラスのジェネリック型シグネチャは随時変更されるため、ランタイムでの原因不明の例外に驚くことはありません。

堅牢なコード

public abstract class Clazz<P extends Params, M extends Model> {

    protected M model;

    protected void createModel() {
    Type[] typeArguments = ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments();
    for (Type type : typeArguments) {
        if ((type instanceof Class) && (Model.class.isAssignableFrom((Class) type))) {
            try {
                model = ((Class<M>) type).newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
                throw new RuntimeException(e);
            }
        }
    }
}

またはワンライナーを使用

1行のコード

model = ((Class<M>) ((ParameterizedType) this.getClass().getGenericSuperclass()).getActualTypeArguments()[1]).newInstance();

2

あなたができることは-

  1. まず、そのジェネリッククラスの変数を宣言します

    2.次に、そのコンストラクタを作成し、そのオブジェクトをインスタンス化します

  2. 次に、使用したい場所で使用します

例-

1

private Class<E> entity;

2

public xyzservice(Class<E> entity) {
        this.entity = entity;
    }



public E getEntity(Class<E> entity) throws InstantiationException, IllegalAccessException {
        return entity.newInstance();
    }

3。

E e = getEntity(entity);


0

あなたが言ったように、型消去のためにあなたは本当にそれを行うことができません。リフレクションを使用してそれを行うことができますが、多くのコードと多くのエラー処理が必要です。


リフレクションをどのように使用しますか?私が見る唯一のメソッドはClass.getTypeParameters()ですが、それは宣言された型のみを返し、実行時の型は返しません。
David Citron

これについて話していますか?artima.com/weblogs/viewpost.jsp?thread=208860
David Citron

0

もしそうなら new E() 、それは不可能です。そして、私はそれが常に正しいとは限らないことを付け加えます-Eに引数のないパブリックコンストラクターがあるかどうかをどのようにして知るのですか?それができる-しかし、あなたは常にいくつかの他のインスタンスを作成する方法を知っているクラスに作成を委任することができClass<E>、このようにしたり、カスタムコード

interface Factory<E>{
    E create();
}    

class IntegerFactory implements Factory<Integer>{    
  private static int i = 0; 
  Integer create() {        
    return i++;    
  }
}

0
return   (E)((Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();

1
これは、元の質問の私の例では機能しません。のスーパークラスSomeContainerは単純Objectです。したがって、ではなく(クラスjava.lang.Object)をthis.getClass().getGenericSuperclass()返します。これは実際には既にピアアンサーstackoverflow.com/questions/75175/…でも指摘されています。ClassParameterizedType
David Citron、2011

1
完全に間違っています:スレッド "main"の例外java.lang.ClassCastException:java.lang.Classはjava.lang.reflect.ParameterizedTypeにキャストできません
Aubin

0

これは、次のスニペットで実現できます。

import java.lang.reflect.ParameterizedType;

public class SomeContainer<E> {
   E createContents() throws InstantiationException, IllegalAccessException {
      ParameterizedType genericSuperclass = (ParameterizedType)
         getClass().getGenericSuperclass();
      @SuppressWarnings("unchecked")
      Class<E> clazz = (Class<E>)
         genericSuperclass.getActualTypeArguments()[0];
      return clazz.newInstance();
   }
   public static void main( String[] args ) throws Throwable {
      SomeContainer< Long > scl = new SomeContainer<>();
      Long l = scl.createContents();
      System.out.println( l );
   }
}

4
完全に間違っています:スレッド "main"の例外java.lang.ClassCastException:java.lang.Classはjava.lang.reflect.ParameterizedTypeにキャストできません
Aubin

0

ERobertsonの記事で説明したのと同様の手法を使用して解決できるさまざまなライブラリがあります。TypeToolscreateContents使用して、Eで表される生のクラスを解決するの実装は次のとおりです。

E createContents() throws Exception {
  return TypeTools.resolveRawArgument(SomeContainer.class, getClass()).newInstance();
}

これは、getClass()がSomeContainerのサブクラスに解決され、サブクラスでキャプチャされない場合、Eの実際のパラメーター化された値が実行時に消去されるため、失敗することを前提としています。


0

TypeToolscreateContents使用して、によって表される生のクラスを解決するための実装は次のとおりです。E

E createContents() throws Exception {
  return TypeTools.resolveRawArgument(SomeContainer.class, getClass()).newInstance();
}

このアプローチは、SomeContainerがサブクラス化されている場合にのみ機能するため、の実際の値はE型定義に取り込まれます。

class SomeStringContainer extends SomeContainer<String>

それ以外の場合、Eの値は実行時に消去され、回復できません。


-1

クラスローダーとクラス名を使用して、最終的にいくつかのパラメーターを使用できます。

final ClassLoader classLoader = ...
final Class<?> aClass = classLoader.loadClass("java.lang.Integer");
final Constructor<?> constructor = aClass.getConstructor(int.class);
final Object o = constructor.newInstance(123);
System.out.println("o = " + o);

これは、単にクラスオブジェクトを渡すよりも悪い
newacct 14

クラスローダーを明示的に参照する必要はまったくありません。
ステファンライヒ2017

-1

ここに基づいて、改善されたソリューションがあります ParameterizedType.getActualTypeArguments @ noah、@ Lars Bohl、その他によってすでに言及されているにです。

実装の最初の小さな改善。ファクトリはインスタンスではなくタイプを返す必要があります。使用しClass.newInstance()てインスタンスを返すとすぐに、使用範囲を減らします。引数なしのコンストラクタのみがこのように呼び出すことができるためです。より良い方法は、型を返し、クライアントが呼び出したいコンストラクタを選択できるようにすることです。

public class TypeReference<T> {
  public Class<T> type(){
    try {
      ParameterizedType pt = (ParameterizedType) this.getClass().getGenericSuperclass();
      if (pt.getActualTypeArguments() == null || pt.getActualTypeArguments().length == 0){
        throw new IllegalStateException("Could not define type");
      }
      if (pt.getActualTypeArguments().length != 1){
        throw new IllegalStateException("More than one type has been found");
      }
      Type type = pt.getActualTypeArguments()[0];
      String typeAsString = type.getTypeName();
      return (Class<T>) Class.forName(typeAsString);

    } catch (Exception e){
      throw new IllegalStateException("Could not identify type", e);
    }

  }
}

以下に使用例を示します。@Lars Bohlは、拡張を介してジェネリックを具体化するための重要な方法のみを示しました。@noahは、{}。両方のケースを示すテストは次のとおりです。

import java.lang.reflect.Constructor;

public class TypeReferenceTest {

  private static final String NAME = "Peter";

  private static class Person{
    final String name;

    Person(String name) {
      this.name = name;
    }
  }

  @Test
  public void erased() {
    TypeReference<Person> p = new TypeReference<>();
    Assert.assertNotNull(p);
    try {
      p.type();
      Assert.fail();
    } catch (Exception e){
      Assert.assertEquals("Could not identify type", e.getMessage());
    }
  }

  @Test
  public void reified() throws Exception {
    TypeReference<Person> p = new TypeReference<Person>(){};
    Assert.assertNotNull(p);
    Assert.assertEquals(Person.class.getName(), p.type().getName());
    Constructor ctor = p.type().getDeclaredConstructor(NAME.getClass());
    Assert.assertNotNull(ctor);
    Person person = (Person) ctor.newInstance(NAME);
    Assert.assertEquals(NAME, person.name);
  }

  static class TypeReferencePerson extends TypeReference<Person>{}

  @Test
  public void reifiedExtenension() throws Exception {
    TypeReference<Person> p = new TypeReferencePerson();
    Assert.assertNotNull(p);
    Assert.assertEquals(Person.class.getName(), p.type().getName());
    Constructor ctor = p.type().getDeclaredConstructor(NAME.getClass());
    Assert.assertNotNull(ctor);
    Person person = (Person) ctor.newInstance(NAME);
    Assert.assertEquals(NAME, person.name);
  }
}

注:このクラスを抽象化することで、インスタンスの作成時にTypeReference常にクライアントに使用を強制できます。消去したテストケースを表示するためだけに、私はそれをしていません。{}public abstract class TypeReference<T>

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.