在Java中实例化一个generics类
我知道Java的generics要比.Net差一些。
我有一个generics类Foo<T>
,我真的需要使用无参数的构造函数在Foo
实例化T
如何解决Java的限制?
一种select是传入Bar.class
(或者你感兴趣的任何types – 以任何方式指定合适的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"); } }
另一个select是拥有一个“工厂”接口,并将工厂传递给generics类的构造函数。 这更加灵活,你不需要担心reflectionexception。
正如Jon Skeet所说 ,这是工厂的实施:
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
这是一个非常人为的方式来做到这一点,而不需要明确使用构造函数参数。 你需要扩展一个参数化的抽象类。
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..."); } }
我真的需要使用无参数的构造函数在Foo中实例化T
简单的答案是“你不能这样做”Java使用types擦除implment仿制药,这将阻止你这样做。
如何解决Java的限制?
一种方法(可能有其他方法)是将要传递给T的实例的对象传递给Foo<T>
的构造函数。 或者你可以有一个方法setBar(T theInstanceofT);
得到你的T,而不是在自己的类中实例化。
Java中的generics通常比C#更强大。
如果你想构造一个对象,但是没有硬连线的构造函数/静态方法,使用抽象工厂。 您应该能够在任何基本的devise模式书,面向对象的介绍或各种网页中find抽象工厂模式的详细信息和教程。 除了提到Java的闭包语法之外,这里不值得重复代码。
IIRC,C#有一个特殊的情况,指定一个genericstypes有一个无参数的构造函数。 根据定义,这种不规则性的前提是客户端代码想要使用这种特定的构造forms,并鼓励可变性。
使用reflection这只是错误的。 Java中的generics是编译时的静态types特征。 试图在运行时使用它们清楚地表明出了什么问题。 reflection导致冗长的代码,运行时失败,未经检查的依赖和安全漏洞。 ( Class.forName
是特别邪恶的。)
从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); } } }
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); } } }
我可以在JUnittesting设置中执行此操作。
我想testing一个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(); }
快速解决scheme,为我工作。 我看到已经有了这个答案,这可能不是最好的办法。 另外,对于我的解决scheme,你需要Gson 。
但是,我遇到了一个情况,我需要创build一个java.lang.reflect.Type
types的generics类的实例。
以下代码将使用空实例variables创build所需类的实例。
T object = new Gson().fromJson("{}", myKnownType);
其中myKnownType
在手之前是已知的,并通过TypeToken.getType()
获得。
您现在可以在此对象上设置适当的属性。 再一次,这可能不是最好的办法,但如果这是你所需要的,它可以作为一个快速的解决scheme。