您说得对。您不能使用new E()
。但您可以将其更改为
private static class SomeContainer<E> {
E createContents(Class<E> clazz) {
return clazz.newInstance();
}
}
虽然有些麻烦,但这个方法有效。将其套用工厂模式可以使其更容易管理。
Class<?>
引用了。请参见此答案以获取代码和链接! - user177800在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
是一个构造函数引用。
如果您的构造函数需要参数,可以改用 lambda 表达式:
SomeContainer<BigInteger> bigIntegerContainer
= new SomeContainer<>(() -> new BigInteger(1));
SomeContainer stringContainer = new SomeContainer(String::new);
?” - Aaron Franke我不知道这是否有帮助,但当您子类化(包括匿名子类化)一个泛型类型时,类型信息可通过反射获得。例如:
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 );
但这需要大量的工作,而且只适用于子类。但可能会很方便。
Foo
类不是抽象的情况。但为什么它只适用于Foo
的匿名子类?假设我们将Foo
变成具体类(去掉abstract
),为什么new Foo<Bar>();
会导致错误,而new Foo<Bar>(){};
则不会呢?(异常:“无法将类转换为ParameterizedType”) - Tim Kuipersclass Foo<E>
中的 <E>
没有绑定到任何特定的类型。只要 E
没有 静态 绑定,就会看到异常行为,例如:new Foo<Bar>()
,new Foo<T>() {...}
或 class Fizz <E> extends Foo<E>
。第一种情况在编译时被 擦除 了,没有静态绑定。第二种情况会用另一个类型变量 (T) 替换 E
,但仍然没有绑定。最后一种情况显然 E
仍未绑定。 - William Priceclass Fizz extends Foo<Bar>
-- 在这种情况下,使用 Fizz
的用户得到的是一个 Foo<Bar>
,除了 Foo<Bar>
之外,它不能是任何其他东西。因此,在这种情况下,编译器很高兴将该信息编码到 Fizz
的类元数据中,并将其作为 ParameterizedType
提供给反射代码。当你创建一个匿名内部类,比如 new Foo<Bar>() {...}
,它也做着同样的事情,只不过编译器生成一个“匿名”的类名,直到外部类被编译才会知道。 - William PriceFoo<Bar<Baz>>
。你将创建一个无法明确创建的ParameterizedTypeImpl
实例。因此,最好检查getActualTypeArguments()[0]
是否返回ParameterizedType。如果是,则要获取原始类型,并创建该类型的实例。 - crushinterface Factory<E> {
E create();
}
class SomeContainer<E> {
private final Factory<E> factory;
SomeContainer(Factory<E> factory) {
this.factory = factory;
}
E createContents() {
return factory.create();
}
}
Factory<>
是一个接口,因此没有具体实现。重点是需要通过间接层传递任务到那些懂得如何构造实例的方法中去。最好使用普通代码来完成这个过程,而不是使用元语言的Class
或Constructor
,因为反射会带来一系列麻烦。 - Tom Hawtin - tacklineSomeContainer<SomeElement> cont = new SomeContainer<>(SomeElement::new);
- Liipackage 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());
}
}
class GenericHome<T> extends Home<T>{}
。 - Holger如果你需要在泛型类中创建一个新的类型参数实例,则可以通过在构造函数中要求其类来实现...
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);
优点:
缺点:
Foo<L>
。首先...如果类型参数类没有默认构造函数,newInstance()
会抛出异常。不过,这个问题在所有已知的解决方案中都存在。现在您可以这样做,而且不需要大量的反射代码。
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()) {};
}
}
摘自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);
cls.newInstance()
已经被弃用,推荐使用cls.getDeclaredConstructor().newInstance()
。 - antikbd考虑一种更加功能化的方法:不要从无中创造出某个E(这显然是代码异味),而是传递一个知道如何创建E的函数,即
E createContents(Callable<E> makeone) {
return makeone.call(); // most simple case clearly not that useful
}
Supplier<E>
来避免捕获 Exception
。 - Martin D在编译时,当您使用E时,您实际上并不关心实际的通用类型“E”(无论是使用反射还是使用通用类型的基类),因此让子类提供E的实例。
abstract class SomeContainer<E>
{
abstract protected E createContents();
public void doWork(){
E obj = createContents();
// Do the work with E
}
}
class BlackContainer extends SomeContainer<Black>{
protected Black createContents() {
return new Black();
}
}
add(...)
和set(0,...)
以检查是否为空的通用列表编写断言时。 - Egor Hans