Home > front end >  Java: why am I able to compile passing unbounded generic as a bounded parameter?
Java: why am I able to compile passing unbounded generic as a bounded parameter?

Time:09-17

I stumbled upon this issue with JavaFx, but it could be simplified to the following compilable launchable code. But shouldn't it fail compile-time? The only thing we can be sure about test2() is that it returns Object or null, it's <V(extends Object)>, not <V extends IDummy>. Why am I able to compile it?

public class Main {    
    public static void main(String[] args) {
        test(test2());
    }
    
    private static void test(IDummy prop) {}
    
    private static <V> V test2(){
        return null;
    }
    
    public interface IDummy {}
}

CodePudding user response:

<V> V test2() says "this is a method that accepts an unbounded type parameter V and returns an object of that type".

Let's ignore for a second how the implementation of that would look like (we'll get to that later).

So if you call test(test2()) the compiler knows that the result of test2() should be an IDummy. Is there a way to ensure that? Yes, there is: just pass IDummy as the concrete type parameter (Main.<IDummy>test2() would be the explicit way to write it, but the compiler infers that for you).

So the compiler knows that you want to use IDummy for V and automagically does it for you. Therefore the whole statement compiles.

Now let's go back to the issue of implementing test2() in a meaningful way. There's really only 2 possible ways to implement it:

  • return null (because that's a valid value for every type that V could represent) or
  • cheat by using an unchecked cast.

The second option would be something like writing

V value = (V) new Object(); // or any other value
return value;

This would compile with a warning. The warning basically means that the type guarantees by generics are broken by this.

Sometimes that can be useful when you know that the inferred type is usually correct (for example if you had <V> V getProperty(String propertyName), then as long as every call had the correct propertyName/type combination it would actually work). But it's not strictly correct and you're basically asking the compiler to "trust me". Any breakage will result in runtime exceptions (most likely a ClassCastException eventually).

CodePudding user response:

I think it's due to compiled type erasure. Looking at the class file, the test2() method should return IDummy. Finally, I recommend learning more knowledge about generics.

  • Related