626

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

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

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

誰かが Ian Robertson のArtima Articleとは劇的に異なる何かを考え出すかどうかを確認するために、これをしばらく開いたままにしておきます。

4

29 に答える 29

361

あなたは正しいです。できませんnew E()。しかし、あなたはそれを変更することができます

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

痛いです。しかし、それは機能します。工場出荷時のパターンでラップすると、もう少し許容範囲が広がります。

于 2008-09-16T18:10:01.820 に答える
140

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));
于 2016-03-30T16:51:40.597 に答える
139

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

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

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

于 2008-09-16T18:18:29.880 に答える
89

お金を渡すには、何らかの種類の抽象ファクトリが必要です。

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();
    }
}
于 2008-09-16T18:36:12.570 に答える
26
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());
    }

}
于 2010-09-03T12:41:57.437 に答える
24

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

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のSuperTypeToken(STT)アプローチよりもはるかに単純(そして問題が少ない)です。
  • STTアプローチ(朝食に携帯電話を食べる)よりもはるかに効率的です。

短所:

  • Classをデフォルトのコンストラクターに渡すことはできません(これがFooがfinalである理由です)。本当にデフォルトのコンストラクターが必要な場合は、いつでもsetterメソッドを追加できますが、後で彼女に電話をかけることを忘れないでください。
  • ロバートソンの異議...黒い羊よりも多くのバー(ただし、型引数クラスをもう一度指定しても、正確にあなたを殺すことはありません)。そして、Robertsonの主張に反して、コンパイラが型の正確さを保証するので、これはとにかくDRYプリンシパルに違反しません。
  • 完全にFoo<L>証明されているわけではありません。手始めに...newInstance()型引数クラスにデフォルトのコンストラクターがない場合、wobblerがスローされます。とにかく、これはすべての既知のソリューションに当てはまります。
  • STTアプローチの完全なカプセル化が欠けています。ただし、大したことではありません(STTの法外なパフォーマンスオーバーヘッドを考慮すると)。
于 2013-01-07T07:11:23.823 に答える
23

これはすぐに実行でき、大量のリフレクション コードは必要ありません。

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

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

TypeTokenのJavaDoc は次のとおりです。

于 2014-08-08T02:03:27.300 に答える
16

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.getDeclaredConstructor().newInstance();   // OK
    list.add(elem);
}

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

List<String> ls = new ArrayList<>();
append(ls, String.class);
于 2012-09-13T13:12:51.680 に答える
7

次のように、インスタンス化中にクラス名を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();
    }

}
于 2008-09-17T20:19:51.860 に答える
7

これが私が思いついたオプションです、それは役立つかもしれません:

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();
}
于 2008-09-16T18:14:49.030 に答える
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);
}

次のように追加メソッドを呼び出すことができます。

List<String> ls = new ArrayList<>();
append(ls, String.class);
于 2016-02-10T12:32:49.873 に答える
5

以下を使用できます。

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

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

于 2008-12-17T22:09:03.157 に答える
3

私はそれができると思っていましたが、かなりがっかりしました。うまくいきませんが、それでも共有する価値があると思います.

たぶん誰かが修正できます:

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によるものです

于 2013-01-03T20:13:41.280 に答える
3

@Noahの回答の改善。

変更理由

a]順序を変更した場合に備えて、複数のジェネリック型を使用すると安全です。

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();
于 2014-11-07T08:13:49.170 に答える
1

TypeToolscreateContentsを使用してで表される生のクラスを解決するの実装を次に示します。E

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

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

class SomeStringContainer extends SomeContainer<String>

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

于 2014-12-04T20:09:34.057 に答える
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++;    
  }
}
于 2008-09-16T18:42:58.653 に答える
0

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

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

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

于 2014-06-11T23:42:38.887 に答える
0

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

于 2008-09-16T18:06:22.733 に答える
0
return   (E)((Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
于 2011-03-22T09:59:50.937 に答える