Different return value types in implementation of generic methods

后端 未结 1 581
无人共我
无人共我 2021-01-31 16:03

Today I stumbled upon some working Java code I wouldn\'t even have expected to compile. Reduced to its bare minimum, it looks like this:

import java.util.List;

         


        
相关标签:
1条回答
  • 2021-01-31 16:42

    While the first example does compile, it will give an unchecked conversion warning:

    // Type safety: The return type List<?> for foo() from the type C needs
    // unchecked conversion to conform to List<String>
    public List<?> foo()
    {
        return null;
    }
    

    What's happening here is that by declaring type parameters, A.foo() and B.foo() are generic methods. Then, the overriding C.foo() omits that type parameter. This is similar to using a raw type, essentially "opting out" of generic type checking for that method signature. That causes the compiler to use the inherited methods' erasures instead: List<String> foo() and List<Integer> foo() both become List foo(), which can therefore be implemented by C.foo().

    You can see that by keeping the type parameter in the C.foo() declaration, there will be the expected compiler error instead:

    // The return type is incompatible with A.foo()
    public <T> List<?> foo()
    {
        return null;
    }
    

    Likewise, if either of the interface methods don't declare a type parameter, then omitting a type parameter from the override fails to "opt out" of generic type checking for that method, and the return type List<?> remains incompatible.

    This behavior is covered in JLS §8.4.2:

    The notion of subsignature is designed to express a relationship between two methods whose signatures are not identical, but in which one may override the other. Specifically, it allows a method whose signature does not use generic types to override any generified version of that method. This is important so that library designers may freely generify methods independently of clients that define subclasses or subinterfaces of the library.

    Angelika Langer's generics FAQ expands on this behavior in her section Can a non-generic method override a generic one?:

    Now, let us explore an example where non-generic subtype methods override generic supertype methods. Non-generic subtype methods are considered overriding versions of the generic supertype methods if the signatures' erasures are identical.

    Example (of non-generic subtype methods overriding generic supertype methods):

    class Super { 
      public <T> void set( T arg) { ... } 
      public <T> T get() { ... } 
    } 
    class Sub extends Super { 
      public void set( Object arg) { ... } // overrides 
      public Object get() { ... }    // overrides with unchecked warning 
    } 
    

    warning: get() in Sub overrides <T>get() in Super;  
    return type requires unchecked conversion 
    found   : Object 
    required: T 
            public Object get() { 
    

    Here the subtype methods have signatures, namely set(Object) and get() , that are identical to the erasures of the supertype methods. These type-erased signatures are considered override-equivalent.

    There is one blemish in the case of the get method: we receive an unchecked warning because the return types are not really compatible. The return type of the subtype method get is Object , the return type of the supertype method get is an unbounded type parameter. The subtype method's return type is neither identical to the supertype method's return type nor is it a subtype thereof; in both situations the compiler would happily accept the return types as compatible. Instead, the subtype method's return type Object is convertible to the supertype method's return type by means of an unchecked conversion. An unchecked warning indicates that a type check is necessary that neither the compiler nor the virtual machine can perform. In other words, the unchecked operation is not type-safe. In case of the convertible return types someone would have to make sure that the subtype method's return value is type-compatible to the supertype method's return type, but nobody except the programmer can ensure this.

    0 讨论(0)
提交回复
热议问题