Useless expectation from the compiler when working with generics?

A compiler that must translate a generic type or method (in any language, not just Java) has two options in principle:

Code Specialization. The compiler generates a new view for each instance of the generic type or method. For example, the compiler will generate code for a list of integers and extra, another code for a list of strings, a list of dates, a list of buffers, etc.

Code exchange. The compiler generates code for only one representation of a generic type or method and displays all instances of the generic type or method for a unique representation, the executing type validates and converts the types when necessary.

Java uses a code sharing method. I believe that C # follows the code specialization method, so all of the code below is logical according to me using C #.

Assuming this piece of Java code:

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()); } } 

The code sharing method will result in this code after erasing the type :

 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, 5, a.getClass()); } } 

So my question is:

What is the need to clarify this initial roll?

 (T[]) Arrays.copyOf(a, 5, a.getClass()); 

instead of simple (before erasing styles during coding):

 Arrays.copyOf(a, 5, a.getClass()); 

Is this cast really necessary for the compiler?

Well, Arrays.copyOf returns Object[] and cannot be directly referenced to a more specific type without an explicit downcast.

But could the compiler not make an effort in this case, since it deals with a universal type (return type!)?

Indeed, is it not enough for compilers to use explicit casts to the calling method line ?

 (String[])t.toArray(new String[4]); 

UPDATED ------------------------------------------- ------ --------------------

Thanks @ ruach for his answer.

Here's an example that proves that explicit listing, even present at compile time, matters:

 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, 5, Object[].class); } 

Listing T[] is the only way to warn the user that the cast may not be relevant. And indeed, here we end downcast from Object[] to String[] , which leads to a ClassCastException at runtime.

So, to the extent that "is it not enough for compilers to use explicit casting to the method call line", the answer is:

The developer does not cope with this casting, since it is created automatically at the compilation stage, therefore this runtime function does not warn the user to carefully check his code to ensure security before starting the compilation.

In short, this actor is worth attending.

+7
source share
2 answers

There are two problems in your line of reasoning.

One problem is that explicit casts are a compile-time function (part of a static type system) and a run-time function (part of a dynamic type system). At compile time, they convert an expression of one static type to an expression of another static type. At run time, they provide type safety by applying the requirement that a dynamic type is in fact a subtype of this static type. In your example, of course, the runtime function is skipped because erasing means that there is not enough information to force the cast to run at run time. But the compile-time function is still relevant.

Consider this method:

 private void printInt(Number n) { Integer i = (Integer) n; System.out.println(i + 10); } 

Do you think the following should be true:

 Object o = 47; printInt(o); // note: no cast to Number 

on the grounds that foo in any case cast its argument to Integer , so there is no need to require the calling sheets to add it to Number ?

Another problem with your logic is that while erasing and unchecked casts sacrifice a bit of type safety, the compiler compensates for this sacrifice by issuing warnings. If you are writing a Java program that does not give any warnings (or unprocessed), you can be sure that it will not throw a ClassCastException due to implicit, running, compiler-generated downcasts. (I mean, if you don't suppress such warnings, of course.) In your example, you have a method that claims to be generic and claims that its return type matches its parameter type. By providing an explicit cast to T[] , you give the compiler the option to issue a warning and say that it cannot force this request in this place. Without such a cast, there would be no place to warn of a potential resulting ClassCastException in the calling method.

+3
source

Assume (conceptually) that the type "a" is List<String>[] .

We have no way to get the full type a . We resort to a.getClass() , which returns List[] . The copy is also List[] , and you want to pass it to List<String>[] . The compiler cannot explain that the throw is safe. You can reason it because all the elements in the copy have a List<String> . You know better than the compiler, so you need an explicit cast, and you're right to suppress an "unverified" warning.

Despite the fact that your code, like many unverified castes, works flawlessly on Java platforms today, this is theoretically incorrect. The compiler is not frivolous in issuing a warning. But we have no choice.

A root conflict is an attitude towards erasure.

The compiler lives in an ideal world, as if all types are complete types. The compiler does not recognize erasable types. There is so little hope that one day Java will implement an ideal type system by resetting erasure, so the compiler does not work today on the assumption of erasure.

We programmers live in an erased world. We must work with erasable types and pretend that they are full types, since we do not have access to real full types.

Our codes work only in the erased world; if Java once gets rid of erasure, our codes will all break. Casting List[] to List<String>[] ? This is some nonsense! Invalid!

But today we have no choice. Codes that depend on erasure are ubiquitous. This is a huge problem if Java wants to get rid of erasure. Java probably never does. This is damned.

+2
source

All Articles