是否可以在Java中创建泛型类型的实例? 我根据我所看到的是,答案是思维no ( 因为类型擦除 ),但我有兴趣,如果任何人都可以看到我丢失的东西:
class SomeContainer
{
E createContents()
{
return what???
}
}
编辑:事实证明, 超级类型令牌可以用来解决我的问题,但是它需要很多基于反射的代码,如下面的一些答案所示。
我将对此开放一会儿,看看是否有人提出与Ian Robertson的Artima Article截然不同的东西 。
#1楼
您可以使用以下代码段实现此目的:
import java.lang.reflect.ParameterizedType;
public class SomeContainer {
E createContents() throws InstantiationException, IllegalAccessException {
ParameterizedType genericSuperclass = (ParameterizedType)
getClass().getGenericSuperclass();
@SuppressWarnings("unchecked")
Class clazz = (Class)
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楼
您不能创建类型参数的实例。 例如,以下代码会导致编译时错误:
public static void append(List list) {
E elem = new E(); // compile-time error
list.add(elem);
}
解决方法是,可以通过反射创建类型参数的对象:
public static void append(List list, Class cls) throws Exception {
E elem = cls.newInstance(); // OK
list.add(elem);
}
您可以按以下方式调用append方法:
List ls = new ArrayList<>();
append(ls, String.class);
#3楼
我以为我可以做到,但很失望:这行不通,但我仍然值得分享。
也许有人可以纠正:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
interface SomeContainer {
E createContents();
}
public class Main {
@SuppressWarnings("unchecked")
public static SomeContainer createSomeContainer() {
return (SomeContainer) 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 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 {
private Class typeArgumentClass;
public Foo(Class typeArgumentClass) {
this.typeArgumentClass = typeArgumentClass;
}
public void doSomethingThatRequiresNewT() throws Exception {
T myNewT = typeArgumentClass.newInstance();
...
}
}
用法:
Foo barFoo = new Foo(Bar.class);
Foo etcFoo = new Foo(Etc.class);
优点:
比罗伯逊的超级类型令牌(STT)方法简单得多(且问题更少)。
比STT方法(早餐时会吃掉手机)要高效得多。
缺点:
无法将Class传递给默认构造函数(这就是Foo是最终的原因)。 如果确实需要默认构造函数,则可以随时添加setter方法,但必须记住稍后再给她打电话。
罗伯逊的异议...比“败类”多得多的杠铃(尽管再指定一次类型实参类不会完全杀死您)。 与罗伯逊的主张相反,无论如何这都不违反DRY主体,因为编译器将确保类型正确。
不完全是Foo证明。 首先,如果类型实参类没有默认构造函数,则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);