2017-05-25 45 views
1

由于以下原因,Java类Class<T>为Generic会引起混淆。类<T>如何能通用?

试想一类Cake<T>{} 所以,你可以创建 new Cake<Pineapple>new Cake<Apple>等 如果每个类ex 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()); 
    } 
} 

假设菠萝非通用类作为参数类型为蛋糕

public class Pineapple { 

    @Override 
    public String toString() { 
    return "I am a Pineapple"; 
    } 
} 

如果类Class不是通用的,然后通过直觉,会有Pineapple Class对象 但类Class<T>本身是通用的。然后,似乎有可能创建Pineapple的参数化类对象,即Pineapple<Something>

基础假设:每个类只有一个Class对象。关于参数化有什么意义?

+2

我不明白你的问题,你可以包含一些代码吗? –

+1

“类”中的“T”不对应于“蛋糕”中的“T”。前者表示类本身的编译时类型;意思是“Pineapple.class”是“Class ”的一个实例。 – shmosel

+0

“如果class class是通用的,那么创建Apple 或Integer 似乎是一种可能性。”您从开始的位置如何到达这种可能性并不清楚。 – pvg

回答

3

我会尝试从几个不同的方法来解决这个问题;我认为发生了什么事情之间存在根本性的脱节,所以一个分散的方法可能有更好的机会让其全部“点击”。

层级

所以,首先,不要以为苹果具有 Class对象;相反,有一个苹果类,坐在它旁边的是一个描述它的Class对象。但是这个Class对象本身就是存在的,并不是Apple层次结构的一部分。这不是苹果的父母;这是它最好的朋友。

所有类的基类都是Object,而不是Class。现在,如果Object被参数化了,那么你就可以得到你所描述的东西 - 但事实并非如此。由于Class不是Apple层次结构的一部分,因此它在逻辑上并不遵循被参数化的Class使Apple参数化。

依此类推到其他泛型类

Class对象的重点是谈论其他对象 - 告诉我们的东西,如“这是一个T,”或者“这里是你如何创造一件T “。那么,每个实例在编译时如何告诉我们它正在谈论的是什么类型的东西呢?通过参数。

同样,List对象的要点是要谈论其他对象 - 将它们放在一起组成一个组。它还使用泛型参数来描述它所谈论的事物的种类。使用列表,只需通过类型签名就可以知道内容:a List<String>涉及字符串,而List<Integer>涉及整数。那么,以同样的方式,Class<String>谈论字符串,而Class<Integer>谈论整数。 Class被参数化的事实对String或Integer没有影响,而List被参数化。

换句话说:在最高级别,List<String>用字符串做事情。所以做了Class<String>

一些使用情况

最后,可能是考虑为什么类是参数化有帮助。 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演员阵容,但出于同样的原因,这就是为什么使用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); 
} 

当然,这个例子不是很有意思。但我要指出的一件事是,如果没有消费类,你必须将物品投到(T)。由于type erasure,这实际上在运行时是无操作的,并且如果该项是错误的类,则ClassCastException将从consumer代码中奇怪的生成行中进来 - 而不是从tryConsume内部,它很好并且清楚。为了使cast方法实际上演员,并做它有用,你需要consumeClass.cast(item)返回T。要做到这一点,consumeClass必须是Class<T>