java 为什么不能使用“new”运算符创建泛型类型的实例?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/30646486/
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
Why can't you create an instance of a generic type using "new" operator?
提问by traveh
I found a lot of posts about howto overcome this limitation, but none about why this limitation exists (except this one, which just mentions it has to do with type erasure).
我找到了很多关于如何克服这个限制的帖子,但没有说明为什么存在这个限制(除了这个,它只是提到它与类型擦除有关)。
So why can't you create an instance of a generic type?
那么为什么不能创建泛型类型的实例呢?
To clarify, my question is not howit can be done. I know it's possible in C#, so why not in Java? I'm curious about why the Java folks did not implement a similar mechanism? Why force Java developers to use awkward workarounds that have the potential to result in a runtime error? Are there any potential hazards from such a mechanism?
澄清一下,我的问题不是如何做到这一点。我知道在 C# 中是可能的,那么为什么不在 Java 中呢?我很好奇为什么 Java 人没有实现类似的机制?为什么要强迫 Java 开发人员使用可能导致运行时错误的笨拙的解决方法?这种机制是否存在潜在危害?
回答by AdamSkywalker
Short answer:Java is a compiled programming language, which means that your bytecode is constant at runtime. It is impossible to generate bytecode for new E()
if E
is unknown.
简短回答:Java 是一种编译型编程语言,这意味着您的字节码在运行时是不变的。不可能为new E()
ifE
未知生成字节码。
Explanation: Generic information is erased in runtime:
public class Container<E> {
private E item;
public E getItem() {return item;}
}
class BoxWithPresent extends Container<Present> {
}
class SimpleBox extends Container {
}
In bytecode class BoxWithPresent
contains field item
of type Present
, but class SimpleBox
contains field item
of type Object
(because type E
was not specified).
在字节码中,类BoxWithPresent
包含item
type字段Present
,但类SimpleBox
包含item
type字段Object
(因为E
未指定类型)。
Now you write abstract instantiation method:
现在你编写抽象实例化方法:
public class Container<E> {
public <E> E createE() {
return new E(); // imagine if that was allowed
}
}
What bytecode should be generated here? .class
file is generated right now, at compilation time, but we have no idea what is E
type.
这里应该生成什么字节码?.class
文件现在在编译时生成,但我们不知道什么是E
类型。
So.. can new T()
be replaced with new Object()
? Bad idea, class BoxWithPresent
won't like it, because it expects that E
is Present
.
所以..可以new T()
替换为new Object()
?坏主意,班级BoxWithPresent
不会喜欢它,因为它期望E
是Present
.
Can it be replaced with class.newInstance()
? Again no, there is no class
variable in method scope.
可以换成class.newInstance()
吗?再次不,class
方法范围内没有变量。
That's why new E()
is impossible.
But there are workarounds with passing class
as parameter, or extracting generic information.
这就是为什么new E()
是不可能的。
但是有class
作为参数传递或提取通用信息的解决方法。
回答by scottb
The shortest answer is that generic type parameters do not exist at runtime.
最短的答案是泛型类型参数在运行时不存在。
Generics were retrofitted into the Java language in release 5. In order to maintain backward compatibility with the existing code base, they were implemented by erasure.
泛型在第 5 版中被改造成 Java 语言。为了保持与现有代码库的向后兼容性,它们是通过擦除实现的。
Generic type parameters exist in your source code at compile-time, but nearly all evidence of them is removed in the byte code during compilation. This implementation of generics was chosen because it maintained inter-operability between pre-generics code and Java 5+ generic code. Type safety with generics is largely, therefore, a compile-time only phenomenon.If your generic code compileswithout error and without warnings, then you are assured that your code is type safe.
通用类型参数在编译时就存在于源代码中,但在编译期间几乎所有关于它们的证据都在字节码中被删除。选择泛型的这种实现是因为它保持了前泛型代码和 Java 5+ 泛型代码之间的互操作性。因此,泛型的类型安全在很大程度上只是编译时的现象。如果您的通用代码编译时没有错误且没有警告,那么您可以确信您的代码是类型安全的。
Because of erasure, however, there are (as of Java 5) two kinds of types:
然而,由于擦除,有(从 Java 5 开始)两种类型:
Reifiable. For example
String
,Integer
, etc. A reifiable type has the same type information at compile-time as it has at run-time.Non-reifiable. For example
List<String>
,List<T>
, andT
. Non-reifiable types have less type information at run-time that at compile time. In fact, the run-time types of the above areList
,List
, andObject
. During compilation, the generic type information is erased.
可具体化。例如
String
,Integer
等。可具体化的类型在编译时与在运行时具有相同的类型信息。不可具体化。例如
List<String>
,List<T>
,和T
。与编译时相比,不可具体化的类型在运行时具有更少的类型信息。实际上,上面的运行时类型是List
,List
, 和Object
。在编译期间,通用类型信息被擦除。
You cannot use the new
operator with non-reifiable types because there is no type safe way at run-time for the JVM to generate an object of the correct type.
您不能将new
运算符用于不可具体化的类型,因为在运行时没有类型安全的方式让 JVM 生成正确类型的对象。
Source code:
源代码:
T myObject = new T();
The above does not compile. At run-time, T
has been erased.
以上不编译。在运行时,T
已被擦除。
A strategy for circumventing some problems with type erasure and Java generics is to use type tokens. This strategy is implemented in the following generic method that creates a new T
object:
规避类型擦除和 Java 泛型的一些问题的一种策略是使用类型标记。此策略在以下创建新T
对象的通用方法中实现:
public <T> T newInstance(Class<T> cls) {
T myObject = cls.newInstance();
return myObject;
}
The generic method captures the type information from the Class
object that is passed as a parameter. This parameter is called a type token. Unfortunately, type tokens themselves must always be reifiable (because you can't get a Class
object for a non-reifiable type) which can limit their usefulness.
泛型方法从Class
作为参数传递的对象中捕获类型信息。此参数称为类型标记。不幸的是,类型标记本身必须始终是可具体化的(因为您无法获得Class
不可具体化类型的对象),这会限制它们的实用性。
回答by user489041
Creating a class from a generic. Do note that this relies on the class being parametrized. This returns the class object of the generic, through which you can perform further reflection on to create an object.
从泛型创建类。请注意,这依赖于被参数化的类。这将返回泛型的类对象,您可以通过它进行进一步的反射以创建对象。
public static <T> Class<T> getClassFromGeneric(
Object parentObj,
int oridnalParamterizedTypeIndex) throws Exception{
Type[] typeArray = getParameterizedTypeListAsArray(parentObj);
return (Class<T>)typeArray[oridnalParamterizedTypeIndex];
}
public static <T> Type[] getParameterizedTypeListAsArray(Object parentObj){
try{
return ((ParameterizedType) parentObj.getClass()
.getGenericSuperclass())
.getActualTypeArguments();
}
catch(ClassCastException e){
logger.log(Level.SEVERE, "Most likely, somewhere in your inhetirance chain,"
+ "there is a class that uses a raw type and not the generic param."
+ "See: http://stackoverflow.com/questions/23074446/java-lang-classcastexception-java-lang-class-cannot-be-cast-to-java-lang-reflec"
+ " for more info",e);
throw e;
}
}
Usage:
用法:
public class GenericBaseClass<T>{}
public class GenericImpl extends GenericBaseClass<String>{
public static void main(String[] args){
new GenericImpl();
}
public GenericImpl(){
Class tClazz = getClassFromGeneric(this,0);
Constructor constructor = tClazz.getConstructor();
T newT = constructor.newInstance();
}
}
Contrary to popular belief, generic information at the class level is not "erased".
与流行的看法相反,类级别的通用信息不会“删除”。
回答by MadConan
Remember that generic types are about compile-time safety. Compile-time checking of types allows the compiler to give you warnings/errors about issues with your code. This doesn't directly help your question, but it's important to keep the concepts of compile-time and runtime very clear.
请记住,泛型类型与编译时安全有关。编译时类型检查允许编译器向您提供有关代码问题的警告/错误。这对您的问题没有直接帮助,但保持编译时和运行时的概念非常清晰很重要。
You can't say, "return new T()
" because the compiler has no way of knowing how to do that. Without the specific type, the compiler can't know what constructor to call or even if it exists. Also, in order to "new up" an instance of type T
you need something to call. T
is just a symbol. It isn't a Class
or an Object
. Until you give information about what kind of instance T
is (eg List<String>
) the compiler has no way of doing what you are trying to do.
你不能说,“ return new T()
”,因为编译器不知道如何做到这一点。如果没有特定的类型,编译器就无法知道要调用什么构造函数,甚至不知道它是否存在。此外,为了“新建”一个类型的实例,T
您需要调用一些东西。 T
只是一个符号。它不是一个Class
或一个Object
。除非您提供有关实例T
类型(例如List<String>
)的信息,否则编译器无法执行您要执行的操作。
Typing is just a way of ensuring that the types given and returned will match correctly at compile-time. It's when you specify type specifics that you are able to create an instance of it. That's why you usually see completely open types on interfaces (eg List<T>
, Function<T,R>
etc). So implementers of the interface can specify what types will used.
键入只是确保给定和返回的类型在编译时正确匹配的一种方式。当您指定类型细节时,您就可以创建它的实例。这就是为什么您通常会在接口上看到完全开放的类型(例如List<T>
,Function<T,R>
等)。因此接口的实现者可以指定将使用什么类型。
Maybe it helps to think about generics like templates. Not the software development pattern, but the more abstract concept. The concept of a template says there's this structure without any internal details. If you want to create this thing that follows the template, use the template as your starting point, but you mustfill in the details to make the thing. A generic type is sort of like that -- it allows you to structure something but doesn't give any details about what happens in that structure.
也许考虑像模板这样的泛型会有所帮助。不是软件开发模式,而是更抽象的概念。模板的概念表明这种结构没有任何内部细节。如果你想按照模板来创建这个东西,以模板为起点,但你必须填写细节才能制作东西。泛型类型有点像这样——它允许您构建某些东西,但不提供有关该结构中发生的事情的任何详细信息。
I know I struggled quite a bit with this when generics were introduced. At first I found it easiest to write the implementation for specific types first and then abstract it out using generics afterwards. After I got my head around generics, I now find it easier to start with a generified interface and then implement it.
我知道当引入泛型时,我在这方面挣扎了很多。一开始我发现最简单的方法是先为特定类型编写实现,然后再使用泛型将其抽象出来。在我了解泛型之后,我现在发现从泛型接口开始然后实现它更容易。