return (E)((Class)((ParameterizedType)this.getClass().getGenericSuperclass()).getActualTypeArguments()[0]).newInstance();
SomeContainer
的超类只是Object
。因此,this.getClass().getGenericSuperclass()
返回一个Class
(类java.lang.Object),而不是一个ParameterizedType
。实际上,同行回答https://dev59.com/qnVD5IYBdhLWcg3wI3-L#5389482也已经指出了这一点。 - David Citronimport 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 );
}
}
我受到Ira的解决方案的启发,并稍作修改。
abstract class SomeContainer<E>
{
protected E createContents() {
throw new NotImplementedException();
}
public void doWork(){
E obj = createContents();
// Do the work with E
}
}
class BlackContainer extends SomeContainer<Black>{
// this method is optional to implement in case you need it
protected Black createContents() {
return new Black();
}
}
E
实例,您可以在派生类中实现 createContents
方法(或者在不需要它的情况下将其保留未实现)。 implementation("org.objenesis","objenesis", "3.2")
val fooType = Foo::class.java
var instance: T = try {
fooType.newInstance()
} catch (e: InstantiationException) {
// Use Objenesis because the fooType class has not a default constructor
val objenesis: Objenesis = ObjenesisStd()
objenesis.newInstance(fooType)
}
正如您所提到的,泛型无法实例化。在我看来,您需要改变设计并利用工厂方法设计模式。这样,您就不需要将类或方法设置为泛型:
class abstract SomeContainer{
Parent execute(){
return method1();
}
abstract Parent method1();
}
class Child1 extends Parent{
Parent method1(){
return new Parent();
}
}
class Child2 extends Parent{
Parent method1(){
return new Child2();
}
}
如果你指的是 new E()
,那么这是不可能的。我还要补充一点,这并不总是正确的——你怎么知道 E 是否有公共无参构造函数?
但你可以将创建实例的责任委托给其他知道如何创建实例的类——它可以是Class<E>
,也可以是你自己编写的类,就像这样。
interface Factory<E>{
E create();
}
class IntegerFactory implements Factory<Integer>{
private static int i = 0;
Integer create() {
return i++;
}
}
正如你所说,由于类型擦除的原因,你实际上无法真正做到。虽然可以使用反射来实现某种程度上的功能,但这需要大量的代码和错误处理。
这里是一个改进的解决方案,基于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只展示了一种通过扩展获取reified geneneric的方法,@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);
}
}
注意:您可以通过将此类声明为抽象类:public abstract class TypeReference<T>
,强制TypeReference
的客户端在创建实例时始终使用{}
。我没有这样做,只是为了展示被擦除的测试用例。
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);
add(...)
和set(0,...)
以检查是否为空的通用列表编写断言时。 - Egor Hans