在Java中建立泛型類型的實例?

是否能夠在Java中建立泛型類型的實例? 我根據我所看到的是,答案是思惟no由於類型擦除 ),但我有興趣,若是任何人均可以看到我丟失的東西: html

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

編輯:事實證實, 超級類型令牌能夠用來解決個人問題,可是它須要不少基於反射的代碼,以下面的一些答案所示。 java

我將對此開放一下子,看看是否有人提出與Ian Robertson的Artima Article大相徑庭的東西oracle


#1樓

您能夠使用如下代碼段實現此目的: app

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

#2樓

Java教程-泛型限制ide

沒法建立類型參數的實例 函數

您不能建立類型參數的實例。 例如,如下代碼會致使編譯時錯誤: 性能

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

解決方法是,能夠經過反射建立類型參數的對象: ui

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

您能夠按如下方式調用append方法: this

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

#3樓

我覺得我能夠作到,但很失望:這行不通,但我仍然值得分享。 spa

也許有人能夠糾正:

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提供的解決方案


#4樓

若是須要在泛型類中使用類型參數的新實例,則使構造函數須要其類...

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是最終的緣由)。 若是確實須要默認構造函數,則能夠隨時添加setter方法,但必須記住稍後再給她打電話。
  • 羅伯遜的異議...比「敗類」多得多的槓鈴(儘管再指定一次類型實參類不會徹底殺死您)。 與羅伯遜的主張相反,不管如何這都不違反DRY主體,由於編譯器將確保類型正確。
  • 不徹底是Foo<L>證實。 首先,若是類型實參類沒有默認構造函數,則newInstance()將引起搖擺器。 不管如何,這確實適用於全部已知的解決方案。
  • 缺少STT方法的總體封裝。 不過,這沒什麼大不了的(考慮到STT的驚人性能開銷)。

#5樓

您能夠用一個類加載器和類名,最後加上一些參數。

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);
相關文章
相關標籤/搜索