我知道Java的泛型有点不如.Net。
我有一个泛型类Foo<T>
,我真的需要使用无参数构造函数在T
中实例化Foo
。如何解决Java的限制?
答案 0 :(得分:162)
一种选择是传递Bar.class
(或您感兴趣的任何类型 - 指定适当的Class<T>
引用的任何方式)并将该值保存为字段:
public class Test
{
public static void main(String [] args)
throws Exception // Just for simplicity!
{
Generic<Bar> x = new Generic<Bar>(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");
}
}
另一种选择是拥有一个“工厂”接口,并将工厂传递给泛型类的构造函数。这更灵活,您无需担心反射异常。
答案 1 :(得分:46)
这是Factory实现,Jon Skeet suggested:
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
答案 2 :(得分:7)
这是一种相当人为的方法,无需显式使用构造函数参数。您需要扩展参数化的抽象类。
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...");
}
}
答案 3 :(得分:4)
我真的需要使用无参数实例化Foo中的T. 构造
简单的回答是“你不能这样做”java使用类型擦除来实现泛滥,这会阻止你这样做。
如何解决Java的限制?
一种方式(可能有其他方法)是将要传递T实例的对象传递给Foo<T>
的构造函数。或者你可以有一个方法setBar(T theInstanceofT);
来获取你的T而不是在它自己的类中实例化。
答案 4 :(得分:2)
Java中的泛型通常比C#中更强大。
如果要构造对象但不使用构造函数/静态方法硬连线,请使用抽象工厂。您应该能够在任何基本设计模式书,OOP简介或整个互联网上找到关于抽象工厂模式的详细信息和教程。这里不需要复制代码,除了提到Java的闭包语法很糟糕。
IIRC,C#有一个特殊情况,用于指定具有no-args构造函数的泛型类型。根据定义,这种不规则性预先假定客户代码想要使用这种特殊的构造形式并鼓励可变性。
对此使用反射只是错误的。 Java中的泛型是一种编译时静态输入功能。尝试在运行时使用它们可以清楚地表明出现问题。反射会导致冗长的代码,运行时故障,未经检查的依赖项和安全漏洞。 (Class.forName
特别邪恶。)
答案 5 :(得分:1)
来自https://stackoverflow.com/a/2434094/848072。您需要T类的默认构造函数。
import java.lang.reflect.ParameterizedType;
class Foo {
public bar() {
ParameterizedType superClass = (ParameterizedType) getClass().getGenericSuperclass();
Class type = (Class) superClass.getActualTypeArguments()[0];
try {
T t = type.newInstance();
//Do whatever with t
} catch (Exception e) {
// Oops, no default constructor
throw new RuntimeException(e);
}
}
}
答案 6 :(得分:1)
我可以在JUnit测试设置中执行此操作。
我想测试一个Hibernate外观,所以我一直在寻找一种通用的方法来实现它。请注意,Facade还实现了通用接口。这里T是数据库类,U是主键。
Ifacade<T,U>
是使用主键U访问数据库对象T的外观。
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();
}
答案 7 :(得分:0)
对我有用的快速解决方案。 我看到已经有了这个答案,这可能不是最好的方法。另外,对于我的解决方案,您需要Gson。
但是,我遇到了需要创建类型为java.lang.reflect.Type
的泛型类的实例的情况。
以下代码将使用null实例变量创建所需类的实例。
T object = new Gson().fromJson("{}", myKnownType);
事先知道myKnownType
,并通过TypeToken.getType()
获得。
您现在可以在此对象上设置适当的属性。同样,这可能不是最好的方法,但它可以作为一个快速的解决方案,如果这是你需要的。
答案 8 :(得分:0)
答案 9 :(得分:0)
使用Constructor.newInstance
方法。自Java 9以来,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;
}
}