Java:T obj; obj.getClass()的类型是Class <! - ? - >而不是Class <! - ?延伸T - >。为什么?

时间:2010-10-24 23:11:04

标签: java arrays generics

在这样的功能中:

<T> void foo(T obj)

obj.getClass()的类型为Class<?>,而非Class<? extends T>。为什么呢?

以下代码可以正常使用:

String foo = "";
Class<? extends String> fooClass = foo.getClass();

所以T#getClass()的签名似乎返回Class<? extends T>,对吗?

如果T真的是通用的,那么为什么签名会有所不同?

为了克服这个问题(并且让我更清楚我在徘徊),我实现了这个功能:

@SuppressWarnings("unchecked") static <T> Class<? extends T> classOf(T obj) {
    return (Class<? extends T>) obj.getClass();
}

同样问题:为什么需要演员而不是String案例?为什么是 需要SuppressWarnings?是不是总是从代码中清楚地知道它总是能够安全地进行这种演员?

有什么方法可以从Class<? extends T>获得obj吗?如果有,怎么样?如果没有,为什么不呢?

一种方法是使用classOf。那会安全的,对吧?如果这总是安全的并且提供了一种真正获得Class<? extends T>(而不是Class<?>)的安全方法,为什么Java中没有这样的函数?或者有吗?


那个案子怎么样:

<T> void bar(T[] array)

array.getClass().getComponentType()再次返回Class<?>而不是Class<? extends T>。为什么呢?

我已经实现了这个功能:

@SuppressWarnings("unchecked") static <T> Class<? extends T> classOf(T[] array) {
    return (Class<? extends T>) array.getClass().getComponentType();
}

这又是否可以安全使用?


澄清我想知道的更多内容。请考虑以下演示代码:

static interface I<T> {
    Class<? extends T> myClass();
}

static class A implements I<A> {
    public Class<? extends A> myClass() {
        return this.getClass();
    }
}

static <T> void foo(I<T> obj) {
    Class<? extends T> clazz = obj.myClass(); // this works
}

这很好用。但同样不适用于Object#getClass()

为什么不能使用ClassInstance<T>这样的通用接口和函数getClass()以及每个Java对象自动执行此操作?这正是我所讨论的关于从非泛型基类Object扩展的解决方案的改进。

Object作为通用类:

static abstract class Object<T> {
    abstract Class<? extends T> myClass();
}

static class B extends Object<B> {
    public Class<? extends B> myClass() {
        return this.getClass();
    }
}

static <T> void bar(Object<T> obj) {
    Class<? extends T> clazz = obj.myClass(); // this works
}

现在将myClass()视为getClass()并考虑编译器会自动将其添加到每个类。它会解决很多这些铸造问题。

我所说的主要问题是:为什么不这样做?


或者用不同的词再说一遍:Here, I describe in more detail the solution of such classOf function which overcomes the problem.为什么它不是这样的,即为什么原来的功能不是这样的呢?

(我真的不想得到这样的答案:Java现在的工作方式,即从定义此函数的非泛型Object扩展,这使得这不可能。我在问为什么它并没有以某种方式得到不同的解决,以至于它本来是可能的。)

4 个答案:

答案 0 :(得分:6)

基本问题是getClass()不返回类,因为它在Object级别定义。即它被明确定义为扩展对象的类。他们可以将getClass()定义为。

Class<this> getClass() { /**/ }

但是它的

Class<?> getClass()

这意味着泛型不了解getClass返回的内容。

答案 1 :(得分:4)

在Java中,泛型只是更安全开发的源级工具。

JVM对泛型一无所知。 Java编译器抛弃了这些信息,所有泛型类型实际上只是运行时的Object引用。为了弥补编译器插入必要的强制转换。 此过程称为Type Erasure(Google!)。

List<String> x = new ArrayList<String>();
x.add("hello");
String hello = x.get(0);

在运行时成为以下

List x = new ArrayList();
x.add("hello");
String hello = (String) x.get(0);

要解决您的问题,您可以尝试调查数组中的各个元素(arr[0].getClass())。

答案 2 :(得分:3)

这里有几个不完全准确的答案。泛型确实是使用类型擦除实现的,但这并不意味着丢失所有类型信息。编译器会将类型擦除到最低限度。

所以&lt; T extends String&gt;被删除为String;这就是为什么String上的getClass返回Class&lt;? extends String&gt;。你的无界&lt; T&gt;然而被抹去对象;所以getClass返回Class&lt;? extends Object&gt;,即Class&lt;?&gt;

泛型是复杂的,它们并不总能做你想要的,但有很多方法可以解决很多问题(通过改进边界,通过反射访问运行时类型信息,以及传递类对象)。类型擦除实际上是一个非常聪明的解决方案,不值得它收到的大部分坏消息。

答案 3 :(得分:0)

由于类型擦除,运行时不保留通用数组的类型信息。实际上,JVM在内部将所有通用数组视为Object []。

如果要获取运行时类型,最好的选择可能只是在数组中的第一项上调用getClass()。您显然需要找到一种处理空案例的方法,以及包含的对象有多种类型的情况等。