在 Java 中实例化一个泛型类
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/1090458/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Instantiating a generic class in Java
提问by ripper234
I know Java's generics are somewhat inferior to .Net's.
我知道 Java 的泛型比 .Net 的要差一些。
I have a generic class Foo<T>
, and I really need to instantiate a T
in Foo
using a parameter-less constructor. How can one work around Java's limitation?
我有一个泛型类Foo<T>
的,我真的需要一个实例T
在Foo
使用参数的构造函数。如何解决 Java 的局限性?
采纳答案by Jon Skeet
One option is to pass in Bar.class
(or whatever type you're interested in - any way of specifying the appropriate Class<T>
reference) and keep that value as a field:
一种选择是传入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");
}
}
Another option is to have a "factory" interface, and you pass a factory to the constructor of the generic class. That's more flexible, and you don't need to worry about the reflection exceptions.
另一种选择是拥有一个“工厂”接口,并将工厂传递给泛型类的构造函数。这样更灵活,您无需担心反射异常。
回答by hhafez
I really need to instantiate a T in Foo using a parameter-less constructor
我真的需要使用无参数构造函数在 Foo 中实例化一个 T
Simple answer is "you cant do that" java uses type erasure to implment generics which would prevent you from doing this.
简单的答案是“你不能那样做”java 使用类型擦除来实现泛型,这会阻止你这样做。
How can one work around Java's limitation?
如何解决 Java 的局限性?
One way (there could be others) is to pass the object that you would pass the instance of T to the constructor of Foo<T>
. Or you could have a method setBar(T theInstanceofT);
to get your T instead of instantiating in the class it self.
一种方法(可能还有其他方法)是将您将 T 的实例传递给Foo<T>
. 或者你可以有一个方法setBar(T theInstanceofT);
来获取你的 T 而不是在它自己的类中实例化。
回答by Tom Hawtin - tackline
Generics in Java are generally more powerful than in C#.
Java 中的泛型通常比 C# 中的更强大。
If you want to construct an object but without hardwiring a constructor/static method, use an abstract factory. You should be able to find detailed information and tutorials on the Abstract Factory Pattern in any basic design patterns book, introduction to OOP or all over the interwebs. It's not worth duplicating code here, other than to mention that Java's closure syntax sucks.
如果你想构造一个对象但没有硬连接构造函数/静态方法,请使用抽象工厂。您应该能够在任何基本设计模式书籍、OOP 介绍或整个互联网上找到有关抽象工厂模式的详细信息和教程。除了提到Java的闭包语法很糟糕之外,这里不值得复制代码。
IIRC, C# has a special case for specifying a generic type has a no-args constructor. This irregularity, by definition, presupposes that client code wants to use this particular form of construction and encourages mutability.
IIRC,C# 有一个特殊情况,用于指定具有无参数构造函数的泛型类型。根据定义,这种不规则性的前提是客户端代码想要使用这种特殊的构造形式并鼓励可变性。
Using reflection for this is just wrongheaded. Generics in Java are a compile-time, static-typing feature. Attempts to use them at runtime are a clear indication of something going wrong. Reflection causes verbose code, runtime failures, unchecked dependencies and security vulnerabilities. (Class.forName
is particularly evil.)
为此使用反射是错误的。Java 中的泛型是一个编译时的静态类型特性。在运行时尝试使用它们是出现问题的明确指示。反射会导致冗长的代码、运行时失败、未经检查的依赖关系和安全漏洞。(Class.forName
特别邪恶。)
回答by Glenn
Here's a rather contrived way to do it without explicitly using an constructor argument. You need to extend a parameterized abstract class.
这是一种相当人为的方法,无需显式使用构造函数参数。您需要扩展参数化抽象类。
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...");
}
}
回答by ?zgür
And this is the Factory implementation, as Jon Skeet suggested:
这是工厂实现,正如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());
}
}
Output:
输出:
Constructor with Factory<T> parameter
Abubeee
回答by albfan
From https://stackoverflow.com/a/2434094/848072. You need a default constructor for T class.
来自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);
}
}
}
回答by Krauss
I could do this in a JUnit Test Setup.
我可以在 JUnit 测试设置中做到这一点。
I wanted to test a Hibernate facade so I was looking for a generic way to do it. Note that the facade also implements a generic interface. Here T is the database class and U the primary key.
Ifacade<T,U>
is a facade to access the database object T with the primary key U.
我想测试一个 Hibernate 外观,所以我正在寻找一种通用的方法来做到这一点。请注意,外观还实现了通用接口。这里 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();
}
回答by Markymark
Quick solution that worked for me. I see there is already an answer for this and this may not even be the best way to go about it. Also, for my solution you'll need Gson.
对我有用的快速解决方案。我看到已经有一个答案,这甚至可能不是解决它的最佳方法。另外,对于我的解决方案,您将需要Gson。
However, I ran into a situation where I needed to create an instance of a generic class of type java.lang.reflect.Type
.
但是,我遇到了需要创建类型为泛型类的实例的情况java.lang.reflect.Type
。
The following code will create an instance of the class you want with null instance variables.
以下代码将使用空实例变量创建您想要的类的实例。
T object = new Gson().fromJson("{}", myKnownType);
Where myKnownType
is known before hand and obtained via TypeToken.getType()
.
哪里myKnownType
事先知道并通过 获得TypeToken.getType()
。
You can now set appropriate properties on this object. Again, this may not be the best way to do this but it works as a quick solution if that's what you need.
您现在可以在此对象上设置适当的属性。同样,这可能不是执行此操作的最佳方法,但如果您需要,它可以作为一种快速解决方案。
回答by Alireza Fattahi
For Java 8 ....
对于 Java 8 ....
There is a good solution at https://stackoverflow.com/a/36315051/2648077post.
https://stackoverflow.com/a/36315051/2648077帖子中有一个很好的解决方案。
This uses Java 8 Supplier
functional interface
这使用 Java 8Supplier
功能接口
回答by Erik Ward Eeshwar Das
Use The Constructor.newInstance
method. The Class.newInstance
method has been deprecated since Java 9 to enhance compiler recognition of instantiation exceptions.
使用Constructor.newInstance
方法。该Class.newInstance
方法自 Java 9 起已被弃用,以增强编译器对实例化异常的识别。
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;
}
}