由于以下原因,作为Generic的Java类Class<T>
令人困惑。
想象一个班级Cake<T>{}
所以你可以创造
new Cake<Pineapple>
和new Cake<Apple>
等
如果每个类Apple
具有可参数化的类对象,并且如果类Class
是通用的,那么创建Apple<Something>
或Integer<Something>
似乎是可能的任何意义。
我不知道我在这里错误地感知到了什么。这似乎是开始,但更深层次与上述水平相同。
假设下面是通用类
public class Cake<T> {
T cakeType;
void setCakeType(T cakeArg){
this.cakeType = cakeArg;
}
void doStuff(){
System.out.println(cakeType.toString());
}
}
假设Pineapple是非泛型类,作为Cake
的参数类型public class Pineapple {
@Override
public String toString() {
return "I am a Pineapple";
}
}
如果类Class
不是通用的,那么凭直觉,会有Pineapple Class
个对象
但是因为类Class<T>
本身是通用的。然后似乎有可能创建Pineapple
的可参数化的类对象,即Pineapple<Something>
。
基本假设:每个类只有一个Class
对象。参数化它有什么意义?
答案 0 :(得分:3)
我会尝试从几种不同的方法来解决这个问题;我认为正在发生的事情之间存在根本性的脱节,因此散射方法可能更有可能使其中一个点全部“点击”。
层次结构
所以,首先,不要认为Apple 拥有一个Class对象;相反,有一个Apple类,并坐在它旁边,一个描述它的Class对象。但是这个Class对象本身就存在,并且不属于Apple的层次结构。这不是Apple的父母;这是它最好的朋友。
所有类的基类是Object,而不是Class。现在,如果对象被参数化,你就会有一些你所描述的东西 - 但事实并非如此。由于Class不是Apple层次结构的一部分,因此从逻辑上讲,参数化的类使得Apple参数化。
类似于其他通用类
Class对象的要点是谈论其他对象 - 告诉我们“这是一个T”或“这就是你如何创建一个T”。那么,每个实例如何在编译时告诉我们它正在讨论哪种事情?通过参数。
类似地,List对象的要点是讨论其他对象 - 将它们放在一起组。它还使用通用参数来描述它所讨论的事物类型。使用List,您可以通过类型签名来判断其中的内容:List<String>
关注字符串而List<Integer>
关注整数。好吧,以同样的方式,Class<String>
谈论字符串,而Class<Integer>
谈论整数。 Class参数化的事实对String或Integer的影响不比List参数化的事实。
换句话说:在最高级别,List<String>
使用字符串做事情。 Class<String>
某些用例
最后,考虑为什么 Class参数化可能会有所帮助。 Class上只有少数方法返回T
类型。我们来看看其中两个:
T newInstance()
T cast(Object o);
如果Class未参数化,则这两个都将返回Object。你必须做这样的事情:
Class<Apple> appleClass = ...;
Apple newApple = (Apple) appleClass.newInstance();
Apple existingApple = (Apple) appleClass.cast(existingObject);
好的,这并不是太糟糕。但在这种情况下,我们已经知道了我们感兴趣的东西(Apple)。这就是为什么我们可以添加Apple演员,但同样的道理,这就是使用Class<Apple>
无效的原因。上面的代码段会更好地完成:
Apple newApple = new Apple();
Apple existingApple = (Apple) existingObject;
相反,当您使用本身通用的方法时,类的通用方面通常很有用。例如:
<T> T tryConsume(Consumer<T> consumer, Object item, Class<T> consumeClass) {
T itemCasted = consumeClass.cast(item);
consumer.consume(itemCasted);
}
当然,这个例子不是很有趣。但我要指出的一件事是,如果没有consumeClass,你必须将项目转换为(T)
。由于type erasure,这在运行时实际上是一个无操作,如果该项是错误的类,则ClassCastException将来自consumer
代码中的一个奇怪的生成行 - 不是来自tryConsume,它很好而且清晰。要使cast
方法实际执行演员,并且有用,您需要consumeClass.cast(item)
才能返回T
。要做到这一点,consumeClass
必须是Class<T>
类型。