web-dev-qa-db-ja.com

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

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

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

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

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

533
David Citron

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

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

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

310
Justin Rudd

これが助けになるならば、しかし、あなたが(匿名で含む)ジェネリック型をサブクラス化するとき、型情報はリフレクションを通して利用可能です。例えば。、

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

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

121
noah

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));
92
Daniel Pryden

次のものに予算を渡すには、何らかの種類の抽象ファクトリーが必要です。

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

}
23
Lars Bohl

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

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

長所:

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

短所:

  • Classをデフォルトのコンストラクタに渡すことはできません(これがFooがfinalである理由です)。デフォルトのコンストラクタが本当に必要な場合は、いつでもsetterメソッドを追加することができますが、後で彼女に呼び出しをすることを忘れないでください。
  • ロバートソンの異議...黒い羊よりも多くのバー(型引数クラスをもう一回指定してもあなたを完全に殺すわけではないが)。 Robertsonの主張に反して、これはDRYプリンシパルに違反しません。コンパイラが型の正しさを保証するからです。
  • 完全にFoo<L>proofではありません。初心者のために... type引数クラスがデフォルトのコンストラクタを持っていない場合、newInstance()は激怒を投げます。とにかくこれはすべての既知の解決策に当てはまります。
  • STTアプローチの完全なカプセル化が欠けています。大したことではありません(STTの法外なパフォーマンスオーバーヘッドを考慮して)。
20
R2D2M2

あなたは今これをすることができます、そしてそれはたくさんのリフレクションコードを必要としません。

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です

18
user177800

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

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

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

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

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

次のようにインスタンス化中にクラス名を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
jb.

残念ながら、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);
5
Neepsnikeep

コンパイル時に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
Ira

TypeToken<T> クラスを使用します。

public class MyClass<T> {
    public T doSomething() {
        return (T) new TypeToken<T>(){}.getRawType().newInstance();
    }
}
3
cacheoff

あなたが使用することができます:

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

しかし、あなたはパッケージを含む正確なクラス名を供給する必要があります。 Java.io.FileInputStream。私はこれを使って数学式パーサを作成しました。

3
Jaroslav Smid

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

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

ワンラインコード

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

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

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

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

Robertsonの記事で議論されているのと同じようなテクニックを使用してあなたのためにEを解決できる様々なライブラリがあります。これは、Eで表される生のクラスを解決するために TypeTools を使用するcreateContentsの実装です。

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

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

0
Jonathan

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

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

class IntegerFactory implements Factory<Integer>{    
  private static int i = 0; 
  Integer create() {        
    return i++;    
  }
}
0
Pavel Feldman
return   (E)((Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
0
Rachid

次のコードでこれを実現できます。

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

これは、 TypeTools を使用してcreateContentsで表される生のクラスを解決するEの実装です。

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

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

class SomeStringContainer extends SomeContainer<String>

そうでなければ、Eの値は実行時に消去され、回復できません。

0
Jonathan

あなたにできることは-

  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
Sudhanshu Jain

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

0
Adam Rosenfield

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

Javaは型保証されており、Objectのみがインスタンスを作成できます。

私の場合は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();
    }
}

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

0
Se Song

これは、@ noah、@ Lars Bohl、その他によって既に言及されているParameterizedType.getActualTypeArgumentsに基づく、改善された解決策です。

実装における最初の小さな改善。 Factoryはインスタンスを返すべきではなく、型を返すべきです。 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>。私はそれをしていない、ただ消去されたテストケースを示すために。

0
Alexandr