Code specialization. The compiler generates a new representation for
every instantiation of a generic type or method. For instance,the
compiler would generate code for a list of integers and additional,
different code for a list of strings,a list of dates,a list of
buffers,and so on.Code sharing. The compiler generates code for only one representation
of a generic type or method and maps all the instantiations of the
generic type or method to the unique representation,performing type
checks and type conversions where needed.
Java使用代码共享方法.我相信C#遵循代码特化方法,所以下面的所有代码都是根据我使用C#的逻辑.
假设这个Java代码片段:
public class Test { public static void main(String[] args) { Test t = new Test(); String[] newArray = t.toArray(new String[4]); } @SuppressWarnings("unchecked") public <T> T[] toArray(T[] a) { //5 as static size for the sample... return (T[]) Arrays.copyOf(a,5,a.getClass()); } }
public class Test { public static void main(String[] args) { Test t = new Test(); //Notice the cast added by the compiler here String[] newArray = (String[])t.toArray(new String[4]); } @SuppressWarnings("unchecked") public Object[] toArray(Object[] a) { //5 as static size for the sample... return Arrays.copyOf(a,a.getClass()); } }
所以我的问题是:
精确初始演员的需要是什么? :
(T[]) Arrays.copyOf(a,a.getClass());
而不是简单地做(在类型擦除之前,在编码时):
Arrays.copyOf(a,a.getClass());
这个演员是否真的对编译器有用?
好的,Arrays.copyOf返回Object []并且在没有显式向下转换的情况下不能通过更具体的类型直接引用.
但是编译器不能在这种情况下努力,因为它处理泛型类型(返回类型!)?
实际上,编译器对方法的调用者行应用显式强制转换是不够的? :
(String[])t.toArray(new String[4]);
更新 – – – – – – – – – – – – – – – – – – – – – – – – – ——————–
感谢@ruakh的回答.
这里的一个示例证明了即使只是在编译时出现的显式转换也是相关的:
public static void main(String[] args) { Test t = new Test(); String[] newArray = t.toArray(new String[4]); } public <T> T[] toArray(T[] a) { return (T[]) Arrays.copyOf(a,Object[].class); }
强制转换为T []是向用户发出警告的唯一方法.实际上,在这里我们最终将Object []转换为String [],这会在运行时导致ClassCastException.
因此,要说“编译器对方法的调用者行应用显式强制转换是不够的”,答案是:
开发人员不会掌握此转换,因为它是在编译步骤中自动创建的,因此在启动编译之前,此运行时功能不会警告用户深入检查其代码的安全性.
简而言之,这个演员阵容值得一提.
解决方法
一个问题是显式强制转换既是编译时特性(静态类型系统的一部分)又是运行时特征(动态类型系统的一部分).在编译时,它们将一个静态类型的表达式转换为另一个静态类型的表达式.在运行时,它们通过强制要求动态类型实际上是该静态类型的子类型来确保类型安全性.当然,在您的示例中,跳过了运行时功能,因为擦除意味着没有足够的信息来在运行时强制执行强制转换.但编译时功能仍然相关.
考虑这种方法:
private void printInt(Number n) { Integer i = (Integer) n; System.out.println(i + 10); }
你认为以下内容应该是有效的:
Object o = 47; printInt(o); // note: no cast to Number
理由是foo会立即将其参数转换为Integer,所以没有必要要求调用者将其转换为Number?
你的推理的另一个问题是,虽然擦除和未经检查的强制转换确实牺牲了一些类型安全性,但编译器会通过发出警告来弥补这种牺牲.如果您编写的Java程序没有提供任何未经检查(或原始类型)的警告,那么您可以确保它不会因为隐式的,仅运行时编译器生成的向下转发而抛出任何ClassCastExceptions. (我的意思是,除非你压制这样的警告,当然.)在你的例子中,你有一个声称是通用的方法,并声称它的返回类型与其参数类型相同.通过向T []提供显式强制转换,您可以让编译器发出警告并告诉您它无法在该位置强制执行该声明.如果没有这样的强制转换,就没有地方可以警告调用方法中潜在的结果ClassCastException.