在这样的功能中:
<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
扩展,这使得这不可能。我在问为什么它并没有以某种方式得到不同的解决,以至于它本来是可能的。)
答案 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()。您显然需要找到一种处理空案例的方法,以及包含的对象有多种类型的情况等。