是否可以在Java中创建泛型类型的实例?我_167905已经看到答案是 no
(由于类型擦除),但我错过了'd be interested if anyone can see something I':
class SomeContainer<E>
{
E createContents()
{
return what???
}
}
编辑:事实证明Super Type Tokens可用于解决我的问题,但它需要大量基于反射的代码,如下面的一些答案所示 .
我是'll leave this open for a little while to see if anyone comes up with anything dramatically different than Ian Robertson' s Artima Article .
23 回答
这是我提出的一个选项,它可能会有所帮助:
编辑:或者你可以使用这个构造函数(但它需要一个E实例):
当您在编译时使用E时,您并不真正关心实际的泛型类型“E”(要么使用反射,要么使用泛型类型的基类),所以让子类提供E的实例 .
来自Java Tutorial - Restrictions on Generics:
Cannot Create Instances of Type Parameters
您无法创建类型参数的实例 . 例如,以下代码导致编译时错误:
作为解决方法,您可以通过反射创建类型参数的对象:
您可以按如下方式调用append方法:
如你所说,由于类型擦除,你无法真正做到这一点 . 您可以使用反射进行排序,但它需要大量代码和大量错误处理 .
你现在可以这样做,它不需要一堆反射代码 .
当然,如果你需要调用需要反思的构造函数,但是这个文档记录很清楚,这个技巧不是!
这是JavaDoc for TypeToken .
您可以使用以下代码段实现此目的:
诺亚的回答是一个改进 .
Reason for Change
a] 如果您更改了订单,则使用超过1种泛型类型会更安全 .
b] 类通用类型签名会不时更改,以便您不会对运行时中无法解释的异常感到惊讶 .
Robust Code
或使用一个衬垫
One Line Code
如果你的意思是
new E()
那么这是不可能的 . 我想补充一点,这并不总是正确的 - 你怎么知道E是否有公共的无参数构造函数?但是您总是可以将创建委托给知道如何创建实例的其他类 - 它可以是Class<E>
或您的自定义代码您可以使用类加载器和类名,最终使用一些参数 .
Java不幸地不允许你想做什么 . 见http://docs.oracle.com/javase/tutorial/java/generics/restrictions.html#createObjects
如果你需要一个泛型类中的类型参数的新实例,那么让你的构造函数需要它的类......
用法:
优点:
比Robertson的超级类型令牌(STT)方法更简单(也更少问题) .
比STT方法更有效率(早餐会吃你的手机) .
缺点:
无法将Class传递给默认构造函数(这就是为什么Foo是最终的) . 如果你确实需要一个默认的构造函数,你总是可以添加一个setter方法,但是你必须记得稍后给她打个电话 .
Robertson 's objection... More Bars than a black sheep (although specifying the type argument class one more time won' t完全杀了你) . 与Robertson的说法相反,这并不违反DRY主体,因为编译器将确保类型正确性 .
不完全
Foo<L>
证明 . 对于初学者...如果类型参数类没有默认构造函数,newInstance()
将抛出一个摇摆器 . 尽管如此,这确实适用于所有已知的解决方案 .缺乏STT方法的完全封装 . 虽然没什么大不了的(考虑到STT的惊人性能开销) .
你是对的 . 你做不到
new E()
. 但你可以改成它这是一种痛苦 . 但它的确有效 . 将其包装在工厂模式中使其更容易忍受 .
您可以使用:
但是你需要提供确切的类名,包括包,例如 .
java.io.FileInputStream
. 我用它来创建一个数学表达式解析器 .我以为我能做到这一点,但很失望:它不起作用,但我认为值得分享 .
也许有人可以纠正:
它产生:
第26行是
[*]
的那一行 .唯一可行的解决方案是@JustinRudd
你需要某种抽象工厂来将这种抽象转移到:
在Java 8中,您可以使用Supplier功能界面轻松实现此目的:
你会像这样构造这个类:
该行的语法
String::new
是constructor reference .如果构造函数接受参数,则可以使用lambda表达式:
这是
createContents
的一个实现,它使用TypeTools来解析由E
表示的原始类:此方法仅在
SomeContainer
被子类化时才有效,因此在类型定义中捕获E
的实际值:否则,E的值在运行时被擦除,不可恢复 .
如果您不希望在实例化期间两次键入类名,如:
您可以使用工厂方法:
像:
Dunno如果这有帮助,但是当您子类化(包括匿名)泛型类型时,类型信息可通过反射获得 . 例如 . ,
所以,当你继承Foo时,你得到一个Bar实例,例如,
但这是很多工作,只适用于子类 . 虽然可以派上用场 .
想想一个更实用的方法:不是从无到有创造一些E(这显然是代码气味),而是传递一个知道如何创建一个的函数,即
有各种各样的库可以使用类似于Robertson文章讨论的技术为您解决
E
. 这是createContents
的实现,它使用TypeTools来解析由E表示的原始类:这假设getClass()解析为SomeContainer的子类,否则将失败,因为如果未在子类中捕获,则E的实际参数化值将在运行时被擦除 .