【问题标题】:Java: T obj; type of obj.getClass() is Class<?> and not Class<? extends T>. why?Java:T 对象; obj.getClass() 的类型是 Class<?> 而不是 Class<?扩展 T>。为什么?
【发布时间】:2011-04-29 23:38:09
【问题描述】:

在这样的函数中:

<T> void foo(T obj)

obj.getClass() 的类型是 Class&lt;?&gt; 而不是 Class&lt;? extends T&gt;。为什么?

以下代码可以正常工作:

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

所以T#getClass() 的签名似乎返回了Class&lt;? extends T&gt;,对吧?

如果T 确实是泛型,为什么签名会有所不同?

为了克服这个问题(并让我更清楚我在游荡什么),我实现了这个功能:

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

又是一个问题:为什么这里需要演员阵容而不是String 案例?为什么是 SuppressWarnings 需要吗?从代码中是否总是清楚地表明它总是能够安全地执行此转换?

有什么方法可以从obj 获得Class&lt;? extends T&gt;?如果是,如何?如果没有,为什么不呢?

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


那个案子怎么样:

<T> void bar(T[] array)

array.getClass().getComponentType() 再次返回 Class&lt;?&gt; 而不是 Class&lt;? extends T&gt;。为什么?

我已经实现了这个功能:

@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&lt;T&gt; 这样的通用接口和函数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 扩展,这使得这不可能。我在问为什么它不是' t 以某种不同的方式解决,所以它本来是可能的。)

【问题讨论】:

标签: java arrays generics


【解决方案1】:

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

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

【讨论】:

  • 但是array.getClass().getComponentType() 会返回给我真正的课程,不是吗?所以它实际上在运行时知道数组类型。
  • 如果您创建了一个特定类型的数组,它将返回真正的类,例如新整数 [] {1,2,3,4}。但如果您创建了一个通用数组,例如类 SomeCollection 中的 T[] - 你只会得到一个 java.lang.Object 数组
  • 是的,如果您调用该示例函数(来自我的原始问题),它必须是特定类型。我也在这里假设这种情况,但我认为甚至不可能没有这种情况。也根本不可能创建一个泛型数组。
【解决方案2】:

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

JVM 对泛型一无所知。 Java 编译器丢弃了这些信息,所有泛型类型在运行时确实只是对象引用。为了弥补这一点,编译器插入了必要的强制转换。 此过程称为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())。

【讨论】:

  • “要解决您的问题,您可以尝试调查数组中的各个元素”。只要您的数组中有 in 元素!但是为演讲+1。
  • 但是数组本身不是也必须知道它是什么吗?它可以抛出ArrayStoreException,因此信息必须在某处。如果它在编译时是T[],理论上编译器将能够以某种方式安全地获得Class&lt;T&gt;
  • Albert:你确定不只是Object[]
  • 次要的挑剔:编译器在类字节码中留下了一些泛型类型信息的残余。具体来说,字段泛型类型、方法泛型类型(参数、返回类型)和超类型泛型类型替换将保留并可访问。但是对象 instances 确实没有通用信息,只有类描述。
【解决方案3】:

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

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

而是它的

Class<?> getClass()

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

【讨论】:

  • “他们本可以定义 [...]”。实际上,您的答案接近我真正想知道的。问题中的为什么为什么不是这样(或以类似方式)完成的,以便我想要的东西成为可能(因为 Java 本身实际上会允许我想要的东西 - 请参阅我的示例)。
  • Java 没有泛型机制来返回this type 的原因。您将不得不询问 Java 的设计者为什么会这样。
  • 我已经实现了接口,如果有这个返回类型,这将非常有用。相反,我不得不使用变数返回类型的方法负载(数百个)
【解决方案4】:

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

所以 被擦除为 String;这就是为什么字符串上的 getClass 返回 Class。但是,您的无限 会被擦除为 Object;所以 getClass 返回 Class extends Object>,即Class>

泛型很复杂,它们并不总能满足您的需求,但有很多方法可以解决很多问题(通过改进边界、通过反射访问运行时类型信息以及传递类对象)。类型擦除实际上是一个非常聪明的解决方案,它不应该受到很多负面报道。

【讨论】:

  • 啊,这很有趣!但这仍然让我想知道为什么会这样(因为我没有看到对getClass() 有这种限制的具体原因,即使是类型擦除也是如此)。我扩展了我最初的问题以说明我的意思。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-26
  • 1970-01-01
  • 2021-09-24
  • 1970-01-01
  • 2018-01-04
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多