在 Java 中实例化泛型类

我知道 Java 的泛型在某种程度上不如.Net。

我有一个泛型类 Foo<T>,我确实需要使用无参数构造函数在 Foo中实例化一个 T。怎样才能绕过 Java 的限制呢?

212245 次浏览

我真的需要在 Foo 中使用无参数实例化 T 构造函数

简单的回答是“你不能这样做”java 使用类型擦除来实现泛型,这将阻止你这样做。

怎样才能绕过 Java 的限制呢?

一种方法(可能还有其他方法)是将 T 的实例传递给 Foo<T>的构造函数的对象。或者您可以使用 setBar(T theInstanceofT);方法来获取 T,而不是在类中实例化它自己。

一种选择是传入 Bar.class(或者任何您感兴趣的类型——任何指定适当 Class<T>引用的方法)并将该值保留为字段:

public class Test {
public static void main(String[] args) throws IllegalAccessException,
InstantiationException {
Generic<Bar> x = new Generic<>(Bar.class);
Bar y = x.buildOne();
}
}


public class Generic<T> {
private Class<T> clazz;


public Generic(Class<T> clazz) {
this.clazz = clazz;
}


public T buildOne() throws InstantiationException, IllegalAccessException {
return clazz.newInstance();
}
}


public class Bar {
public Bar() {
System.out.println("Constructing");
}
}

另一种选择是拥有一个“工厂”接口,然后将一个工厂传递给泛型类的构造函数。这样更灵活,而且不需要担心反射异常。

Java的类型擦除通常比 C # 更强大。

如果您希望构造一个对象,但是不需要硬连接构造函数/静态方法,那么可以使用抽象工厂。您应该能够在任何基本设计模式书籍、面向对象程序设计(OOP)简介或互联网上找到有关抽象工厂模式的详细信息和教程。这里不值得复制代码,除了提到 Java 的闭包语法糟透了。

在 IIRC,C # 有一个特殊的情况,用于指定泛型类型具有 no-args 构造函数。根据定义,这种不规则性预先假定客户端代码希望使用这种特定的构造形式,并鼓励可变性。

用反思来解决这个问题是错误的。Java的类型擦除是一个编译时的静态类型特性。尝试在运行时使用它们是出错的明显迹象。反射会导致冗长的代码、运行时失败、未检查的依赖项和安全漏洞。(Class.forName尤其邪恶。)

这里有一种不显式使用构造函数参数的相当人为的方法。您需要扩展参数化抽象类。

public class Test {
public static void main(String [] args) throws Exception {
Generic g = new Generic();
g.initParameter();
}
}


import java.lang.reflect.ParameterizedType;
public abstract class GenericAbstract<T extends Foo> {
protected T parameter;


@SuppressWarnings("unchecked")
void initParameter() throws Exception, ClassNotFoundException,
InstantiationException {
// Get the class name of this instance's type.
ParameterizedType pt
= (ParameterizedType) getClass().getGenericSuperclass();
// You may need this split or not, use logging to check
String parameterClassName
= pt.getActualTypeArguments()[0].toString().split("\\s")[1];
// Instantiate the Parameter and initialize it.
parameter = (T) Class.forName(parameterClassName).newInstance();
}
}


public class Generic extends GenericAbstract<Foo> {
}


public class Foo {
public Foo() {
System.out.println("Foo constructor...");
}
}

这是 Factory 的实现,作为 乔恩 · 斯基特建议:

interface Factory<T> {
T factory();
}


class Araba {
//static inner class for Factory<T> implementation
public static class ArabaFactory implements Factory<Araba> {
public Araba factory() {
return new Araba();
}
}
public String toString() { return "Abubeee"; }
}


class Generic<T> {
private T var;


Generic(Factory<T> fact) {
System.out.println("Constructor with Factory<T> parameter");
var = fact.factory();
}
Generic(T var) {
System.out.println("Constructor with T parameter");
this.var = var;
}
T get() { return var; }
}


public class Main {
public static void main(String[] string) {
Generic<Araba> gen = new Generic<Araba>(new Araba.ArabaFactory());
System.out.print(gen.get());
}
}

产出:

Constructor with Factory<T> parameter
Abubeee

来自 https://stackoverflow.com/a/2434094/848072. 你需要一个 t 级的缺省构造函数。

import java.lang.reflect.ParameterizedType;


class Foo<T> {
  

public bar() {
ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass();
Class<T> type = (Class<T>) superClass.getActualTypeArguments()[0];
try {
T t = (T) type.getDeclaredConstructor().newInstance();
//Do whatever with t
} catch (Exception e) {
// Oops, no default constructor
throw new RuntimeException(e);
}
}
}

我可以在一个 JUnit 测试安装程序中做到这一点。

我想测试一个 Hibernate facade,所以我在寻找一种通用的方法来实现它。注意,facade 还实现了一个通用接口。这里 T 是数据库类,U 是主键。 Ifacade<T,U>是用主键 U 访问数据库对象 T 的 facade。

public abstract class GenericJPAController<T, U, C extends IFacade<T,U>>


{
protected static EntityManagerFactory emf;


/* The properties definition is straightforward*/
protected T testObject;
protected C facadeManager;


@BeforeClass
public static void setUpClass() {




try {
emf = Persistence.createEntityManagerFactory("my entity manager factory");


} catch (Throwable ex) {
System.err.println("Failed to create sessionFactory object." + ex);
throw new ExceptionInInitializerError(ex);
}


}


@AfterClass
public static void tearDownClass() {
}


@Before
public void setUp() {
/* Get the class name*/
String className = ((ParameterizedType) getClass().getGenericSuperclass()).getActualTypeArguments()[2].getTypeName();


/* Create the instance */
try {
facadeManager = (C) Class.forName(className).newInstance();
} catch (ClassNotFoundException | InstantiationException | IllegalAccessException ex) {
Logger.getLogger(GenericJPAController.class.getName()).log(Level.SEVERE, null, ex);
}
createTestObject();
}


@After
public void tearDown() {
}


/**
* Test of testFindTEntities_0args method, of class
* GenericJPAController<T, U, C extends IFacade<T,U>>.
* @throws java.lang.ClassNotFoundException
* @throws java.lang.NoSuchMethodException
* @throws java.lang.InstantiationException
* @throws java.lang.IllegalAccessException
*/
@Test
public void  testFindTEntities_0args() throws ClassNotFoundException, NoSuchMethodException, InstantiationException, IllegalAccessException {


/* Example of instance usage. Even intellisense (NetBeans) works here!*/
try {
List<T> lista = (List<T>) facadeManager.findAllEntities();
lista.stream().forEach((ct) -> {
System.out.println("Find all: " + stringReport());
});
} catch (Throwable ex) {
System.err.println("Failed to access object." + ex);
throw new ExceptionInInitializerError(ex);
}
}




/**
*
* @return
*/
public abstract String stringReport();


protected abstract T createTestObject();
protected abstract T editTestObject();
protected abstract U getTextObjectIndex();
}

对我有效的快速解决方案。 我看到这个问题已经有了答案,而这甚至可能不是解决这个问题的最佳方法。另外,为了我的解决方案,你需要 葛森

但是,我遇到了需要创建类型为 java.lang.reflect.Type的泛型类的实例的情况。

下面的代码将使用空实例变量创建所需类的实例。

T object = new Gson().fromJson("{}", myKnownType);

其中 myKnownType事先已知,并通过 TypeToken.getType()获得。

现在可以在此对象上设置适当的属性。同样,这可能不是最好的方法,但它作为一个快速的解决方案,如果这是你需要的。

对于 Java 8..。

有一个很好的解决方案在 https://stackoverflow.com/a/36315051/2648077岗位。

这使用 Java8Supplier函数接口

使用 Constructor.newInstance方法。为了增强编译器对实例化异常的识别,从 Java9开始就不推荐使用 Class.newInstance方法。

public class Foo<T> {
public Foo()
{
Class<T> newT = null;
instantiateNew(newT);
}


T instantiateNew(Class<?> clsT)
{
T newT;
try {
newT = (T) clsT.getDeclaredConstructor().newInstance();
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException | SecurityException e) {
// TODO Auto-generated catch block
e.printStackTrace();
return null;
}
return newT;
}
}