In February 2020, Wang Yin trolled the type system of Java, saying.
One of the more advanced interview questions about a programmer’s understanding of the Java type system is this:
Which line in this code is wrong? Why? If some version of Java can run this code without a problem, how can the error be exposed as more fatal? Note that the “wrong” line here is essentially, in principle, wrong.
So what does “error” mean here?
If I could only answer this question in one sentence, it would be:
Java arrays do not support generics, which breaks type safety in Java.
Some prerequisites for a type system
A good type system that detects errors as early as possible , such as when you assign a String to an int variable, the compiler will report an error instead of waiting for the program to run before reporting an error.
What’s wrong with Java’s array design
For the sake of simplicity, let’s assume that Java supports paradigmatic arrays, such as
<? > like this representation.
In the above code, there is actually a hint of something wrong at the second step. Converting a
String to an
Object causes the type details of the array to “escape” from the type system.
Or in more understandable terms: in step 4, the compiler should report an error when stuffing an
String with an Integer object.
If you could start over, how would you design it?
If we follow a perfect type system, Wang Yin’s code should look like this:
The program looks a lot more normal this time, and according to the rules of the Java paradigm, it also triggers a compilation failure in the second step without any problems.
<String> is converted to
<? super String>, which of course doesn’t work, otherwise the type system wouldn’t be able to tell when stuffing Integer objects into it later.
The problem doesn’t end there
<? super String>, essentially for reading: you can read a String as an Object.
The above example, on the other hand, implements writing. Can’t parametric arrays support reading?
Of course not. The upper and lower bounds of the paradigm is used to do these qualifications, the sample code is as follows.
- The lower bound is restricted in the type parameter by
<? super T>restricts the lower bound, then writing is not a problem, you can always write to it as a T type, but reading becomes less likely.
- The upper bound is restricted in the type parameter by
<? extens T>in the type parameter, then reading is not a problem, it can always be read as T, but writing becomes less likely.
Is there a simpler formulation?
In the type system, lists and arrays are similar, and it just so happens that Java’s lists support paradigms, so let’s rewrite the above example with lists.
As you can see, the above program with List, using the type system + the upper and lower bounds of the paradigm, is perfect to limit the type insecurity operation.
However, since the array array does not support paradigms, the JVM can only handle arrays as covariant when implementing them, allowing type-unsafe conversions, resulting in a “loophole” in the Java type system.