我发现了很多关于如何克服此限制的帖子,但没有一个关于为什么存在此限制的帖子(除了这个,它只是提到它与类型擦除有关)。
那么,为什么不能创建泛型类型的实例呢?
澄清一下,我的问题不是如何做到这一点。我知道这在C#中是可能的,那么为什么不在Java呢?我很好奇为什么Java没有实现类似的机制?为什么强迫Java开发人员使用可能导致运行时错误的笨拙变通方法?这种机制有什么潜在的危险吗?
从泛型创建类。请注意,这依赖于被参数化的类。这将返回泛型的类对象,通过它可以执行进一步的反射以创建对象。
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;
}
}
用法:
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();
}
}
与普遍的看法相反,类级别的通用信息并没有被“删除”。
最短的答案是泛型类型参数在运行时不存在。
泛型在第5版中被改造成Java语言。为了保持与现有代码库的向后兼容性,它们通过擦除来实现。
泛型类型参数在编译时存在于源代码中,但在编译期间,它们的几乎所有证据都在字节代码中被移除。选择这种泛型实现是因为它维护了前泛型代码和Java 5泛型代码之间的互操作性。因此,泛型的类型安全在很大程度上只是编译时现象。如果您的泛型代码编译时没有错误和警告,那么您就可以确信您的代码是类型安全的。
然而,由于擦除,有两种类型(从Java 5开始):
>
可赔偿的。例如,String
、Integer
等。可重写类型在编译时具有与运行时相同的类型信息。
不可偿还。例如<code>列表
不能将 new
运算符用于不可重用的类型,因为在运行时,JVM 没有类型安全的方式来生成正确类型的对象。
源代码:
T myObject = new T();
以上不编译。在运行时,< code>T已被擦除。
规避类型擦除和 Java 泛型的一些问题的策略是使用类型标记。此策略在以下用于创建新的 T
对象的泛型方法中实现:
public <T> T newInstance(Class<T> cls) {
T myObject = cls.newInstance();
return myObject;
}
泛型方法从作为参数传递的Class
对象中捕获类型信息。此参数称为类型标记。不幸的是,类型标记本身必须始终是可实现的(因为您无法为不可实现的类型获取Class
对象),这会限制它们的有用性。
简而言之:Java是一种编译的编程语言,这意味着字节码在运行时是恒定的。如果<code>E
解释:通用信息在运行时被删除:
public class Container<E> {
private E item;
public E getItem() {return item;}
}
class BoxWithPresent extends Container<Present> {
}
class SimpleBox extends Container {
}
在字节码中,类< code>BoxWithPresent包含类型< code>Present的字段< code>item,但类< code>SimpleBox包含类型< code>Object的字段< code>item(因为未指定类型< code>E)。
现在您编写抽象实例化方法:
public class Container<E> {
public <E> E createE() {
return new E(); // imagine if that was allowed
}
}
这里应该生成什么字节码?<代码>。class文件是在编译时生成的,但是我们不知道什么是< code>E类型。
所以。。新的 T()
可以用新的 Object() 替换吗
?坏主意,类BoxWithPresent
不会喜欢它,因为它期望E
存在
。
可以用< code>class.newInstance()替换吗?同样,在方法范围内没有< code>class变量。
这就是为什么<code>新的E()但是,通过将<code>类
问题内容: 我读到从Java 7开始,像在第一条语句中那样在右侧指定类型来创建Collections是不好的样式,因为编译器可以从左侧推断类型。 我的问题是,当像这样初始化列表时,编译器找不到类型,并且我收到未经检查的类型警告: 问题答案: 编译器不会 推断 类型,因为您正在实例化 raw 。但是它足够聪明,可以警告您在使用此(原始)对象时可能会出现问题。 值得一提的是此警告背后的原因。由于类型擦
问题内容: 我正在使用泛型编写某些东西,令我惊讶的是,我发现这行不通: 那我不能实例化泛型吗?没有任何方法可以做到这一点吗? 问题答案: 是的,这真是令人讨厌。 我使用的解决方法是强制客户端在构造新类时传递类-即 然后您可以使用。
问题内容: 我有一个通用的抽象模板类。我以为如果创建特定于类型的生产者,则可以直接在通用类中注入一些DAO服务。但是我不能。 为什么?我该如何解决? 当我注入一个例如它完美地工作。但是没有泛型… 问题答案: 当您要求 容器知道您要一个类型为的bean 。如果存在这样的bean并且其类型信息已知,则容器可以满足注入。例如,类型信息保留在带注释的方法中 容器使用反射来检索该参数化的类型,并将其与请求的
问题内容: 下列类定义了两种方法,它们在直观上都具有相同的功能。每个函数都有两个类型和一个布尔值的列表来调用,该值指定应将这些列表中的哪个分配给局部变量。 根据,有效而无效。它抱怨: 我知道查找包含三元运算符()的表达式类型的规则非常复杂,但是据我了解,它选择了最具体的类型,第二个和第三个参数都可以转换为该类型,而无需显式投。在这里,应该是,但不是。 我想解释一下为什么不是这种情况,最好参考 Ja
问题内容: 是否可以在Java中创建泛型类型的实例?我正在根据我所看到的答案进行思考no(由于类型擦除),但是如果有人能看到我所缺少的内容,我将很感兴趣: 编辑:事实证明,超级类型令牌可以用于解决我的问题,但是它需要很多基于反射的代码,如下面的一些答案所示。 问题答案: 你是对的。你做不到。但你可以将其更改为 但这有效。以工厂模式包装它会使它更具容忍性。
如何将Java7类型推断用于泛型实例创建功能?使用这种新样式有什么好处?