我一直在尝试正确地理解Java泛型。因此,在这一探索中,我遇到了一个原则“广告中的真理原则”,我想用简单的语言来理解它。
广告中的真理原则:数组的修饰类型必须是子类型
删除其静态类型。
我已经编写了示例代码.java和.class文件,如下所示。请仔细阅读代码,并解释一下代码中的哪一部分指定/指示了以上声明的哪一部分。
我给我写了评论,我不应该在这里写代码说明。
public class ClassA {
//when used this method throws exception
//java.lang.ClassCastException: [Ljava.lang.Object; cannot be cast to [Ljava.lang.String;
public static <T> T[] toArray(Collection<T> collection) {
//Array created here is of Object type
T[] array = (T[]) new Object[collection.size()];
int i = 0;
for (T item : collection) {
array[i++] = item;
}
return array;
}
//Working fine , no exception
public static <T> T[] toArray(Collection<T> collection, T[] array) {
if (array.length < collection.size()) {
//Array created here is of correct intended type and not actually Object type
//So I think , it inidicates "the reified type of an array" as type here lets say String[]
// is subtype of Object[](the erasure ), so actually no problem
array = (T[]) Array.newInstance(array.getClass().getComponentType(), collection.size());
}
int i = 0;
for (T item : collection) {
array[i++] = item;
}
return array;
}
public static void main(String[] args) {
List<String> list = Arrays.asList("A", "B");
String[] strings = toArray(list);
// String[] strings = toArray(list,new String[]{});
System.out.println(strings);
}
}
请尝试用简单的语言解释。请指出我错了。带有更多注释的更正代码将不胜感激。
谢谢你们
最佳答案
我将Java泛型和集合称为“本书”,将本书的作者称为“作者”。
由于本书在解释IMO原理方面做得很差,因此我会不止一次地对这个问题表示支持。
陈述
广告中的真理原则:
数组的修饰类型必须是擦除其静态类型的子类型。
进一步称为原理。
该原理有何帮助?
词汇
什么是静态类型?
应该称为引用类型。
在下面的代码中,提供的
A
和B
是类型A ref = new B();
A
是ref
的静态类型(B
是ref
的动态类型)。学术界术语。数组的简化类型是什么?
修正是指在运行时可用的类型信息。数组被认为是可修复的,因为VM知道了它们的组件类型(在运行时)。
在
arr2 = new Number[30]
中,arr2
的化类型是Number[]
,其数组类型为组件类型Number
。一种类型的擦除是什么?
应该被称为运行时类型。
类型参数的虚拟机 View (运行时 View )。
提供的
T
是类型参数,以下代码的运行时 View <T extends Comparable<T>> void stupidMethod(T[] elems) {
T first = elems[0];
}
将会void stupidMethod(Comparable[] elems) {
Comparable first = elems[0];
}
这使得Comparable
成为T
的运行时类型。为什么是Comparable
?因为那是T
的最左边界。我要看哪种代码才能使该原则相关?
该代码应暗示分配给数组类型的引用。左值或右值都应包含类型参数。
例如提供的
T
是类型参数T[] a = (T[])new Object[0]; // type parameter T involved in lvalue
或者String[] a = toArray(s); // type parameter involved in rvalue
// where the signature of toArray is
<T> T[] toArray(Collection<T> c);
在左值或右值中不涉及任何类型参数的情况下,该原理无关紧要。示例1(遵循原理)
<T extends Number> void stupidMethod(List<T>elems) {
T[] ts = (T[]) new Number[0];
}
Q1 :ts
所引用的数组的简化类型是什么?A1 :数组创建提供了答案:使用
Number
创建组件类型为new
的数组。 Number[]
。Q2 :
ts
的静态类型是什么?A2 :
T[]
Q3 :静态ts
类型的擦除是什么?A3 :为此,我们需要擦除
T
。鉴于T extends Number
是有界的,所以T
的擦除类型是其最左边的边界-Number
。现在我们知道了T
的擦除类型,对于ts
的擦除类型是 Number[]
Q4 :是否遵循本原则?
A4 :重申问题。 A1 是 A3 的子类型吗?即
Number[]
是Number[]
的子类型吗?是=>这意味着遵循原则。示例2(不遵循原理)
<T extends Number> void stupidMethod(List<T>elems) {
T[] ts = (T[]) new Object[0];
}
Q1 :ts
所引用的数组的简化类型是什么?A1 :使用
new
创建数组,组件类型为Object
,因此为 Object[]
。Q2 :
ts
的静态类型是什么?A2 :
T[]
Q3 :静态ts
类型的擦除是什么?A3 :为此,我们需要擦除
T
。鉴于T extends Number
是有界的,所以T
的擦除类型是其最左边的边界-Number
。现在我们知道了T
的擦除类型,对于ts
的擦除类型是 Number[]
Q4 :是否遵循本原则?
A4 :重申问题。 A1 是 A3 的子类型吗?即
Object[]
是Number[]
的子类型吗?否=>,这意味着未遵循原则。期望在运行时引发异常。
示例3(不遵循原理)
给定提供数组的方法
<T> T[] toArray(Collection<T> c){
return (T[]) new Object[0];
}
客户代码List<String> s = ...;
String[] arr = toArray(s);
Q1 :provider方法返回的数组的简化类型是什么?A1 :为此,您也需要查看提供方法以了解其初始化方式-
new Object[...]
。这意味着该方法返回的数组的修饰类型为 Object[]
。Q2 :
arr
的静态类型是什么?A2 :
String[]
Q3 :静态ts
类型的擦除是什么?A3 :不涉及类型参数。擦除后的类型与静态类型
String[]
相同。Q4 :是否遵循本原则?
A4 :重申问题。 A1 是 A3 的子类型吗?即
Object[]
是String[]
的子类型吗?否=>,这意味着未遵循原则。期望在运行时引发异常。
示例4(遵循原理)
给定提供数组的方法
<T> T[] toArray(Collection<T> initialContent, Class<T> clazz){
T[] result = (T[]) Array.newInstance(clazz, initialContent);
// Copy contents to array. (Don't use this method in production, use Collection.toArray() instead)
return result;
}
客户代码List<Number> s = ...;
Number[] arr = toArray(s, Number.class);
Q1 :provider方法返回的数组的简化类型是什么?A1 :使用从客户端收到的具有组件类型的反射创建的数组。答案是
Number[]
。Q2 :
arr
的静态类型是什么?A2 :
Number[]
Q3 :静态ts
类型的擦除是什么?A3 :不涉及类型参数。擦除后的类型与静态类型
Number[]
相同。Q4 :是否遵循本原则?
A4 :重申问题。 A1 是 A3 的子类型吗?即
Number[]
是Number[]
的子类型吗?是=>这意味着遵循原则。有趣的名字叫什么?
在这里乱逛。广告中的真相可能意味着出售您声明的商品。
在
lvalue = rvalue
我们将rvalue
作为提供者,并将lvalue
作为接收者。作者可能将提供者视为广告商。
参照上述示例3中的提供方法,
<T> T[] toArray(Collection<T> c){
return (T[]) new Object[0];
}
方法签名<T> T[] toArray(Collection<T> c);
可以看成是广告:给我一个Long
的列表,我会给你一个Long
的数组。但是,从方法主体看,实现表明该方法不是真实的,因为它创建并返回的数组是
Object
的数组。因此,示例3中的
toArray
方法在于其营销 Activity 。在示例4中,提供方法是真实的,因为签名中的语句(给我一个集合及其类型参数作为类文字,我将为您提供具有该组件类型的数组)与主体中发生的匹配。
示例3和4具有用作广告的方法签名。
示例1和2没有明确的广告(方法签名)。广告和规定是交织在一起的。
但是,我认为该原则没有更好的名称。那真是个名字。
结束语
由于使用诸如静态类型和擦除类型之类的术语,因此我认为该原则的陈述不必要地是含糊的。分别使用引用类型和擦除后的运行时类型/类型,将使掌握Java外行变得更加容易(真正地像您一样)。
作者指出,这本书是Java泛型上最好的书籍[0]。我认为这意味着他们所针对的受众是广泛的受众,因此,更多有关其所采用原则的示例将非常有帮助。
[0] https://youtu.be/GOMovkQCYD4?t=53
关于java - 理解广告Java泛型中的真理原理,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36589210/