Class <t>如何是通用的?

时间:2017-05-25 01:45:04

标签: java class generics

由于以下原因,作为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对象。参数化它有什么意义?

1 个答案:

答案 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>类型。