String gets assigned to a List without a compilation error [duplicate]
Asked Answered
C

1

24

As I know, one of the main purposes of generics in Java is providing compile-time type safety. If it gets compiled, the code will run without issues.

Then why is the following code being compiled?

public static void main(String[] args) {
    String s = getList();
}

private static <T extends List> T getList() {
    return (T)new ArrayList();
}

It compiles fine. Where is my type-safe compilation? The getList() method has nothing in common with the String class.

Candlestick answered 3/9, 2016 at 10:11 Comment(5)
No, this compiles even when using non-raw types.Lavellelaven
@MarkusFischer, I also thought about that, but the compiler realizes that String is a final class and can't have any subclassesCandlestick
@MarkusFischer, you should transform this comment to an answer. I think that's exactly the issue op observed here. MyClass extends String implements List could be possible at compile time as the compiler doesn't consider String being final, as you said, for those kind of generic-checks.Ommatidium
@AndrewTobilko the compiler only looks at the type, not whether the type is final. Final prevents subclasses from being declared, but does not prevent the compiler from allowing the possibility of subclasses (google Liskov). Put another way, adding or removing final from (non subclassed) classes should not cause compilation failure at their usages. The type hasn't changed, only the restriction on subclassing, which is an implementation thing, not a type thing.Meeks
"If it gets compiled, the code will run without issues." No. You get a warning when you compile this telling you it may not work.Indeterminable
U
15

This is not a type erasure problem per se, but almost the opposite: You get a problem at runtime, when the system knows the actual types, but not at compile time. The reason why this compiles is that List is an interface. As far as the compiler is concerned, a subclass of String might actually implement that interface, so the compiler reasons that there could be valid runtime situations where the actual object returned is a String that is also a List. The compiler does not consider that String is final, and thus that it's impossible to actually create a List-implementing String class.

As to why final is not considered during compilation, Bohemian's comment to the question gives a good explanation.

Universality answered 3/9, 2016 at 20:59 Comment(2)
Should there not at least be a compiler warning? I haven't checked javac itself, but my IDE does not warn about anything but an unchecked cast and raw types. I can work up examples where there are no raw types and where there are no warnings, but where the type is obviously not correct.Osvaldooswal
I agree that a compiler warning would be nice and would make sense for this situation. I don't see one from javac nor Eclipse either. Again, this boils down to how many edge cases the compiler investigates, and apparently a decision was made to not check for this constellation.Universality

© 2022 - 2024 — McMap. All rights reserved.