Search code examples
javatypescastingcovariance

covariant return types and primitives in Java


In OCP book I have read that there is this rule for covariance:

Given an inherited return type A and an overriding return type B, can you assign an instance of B to a reference variable for A without a cast? If so, then they are covariant. This rule applies to primitive types and object types alike.

If the rule applies to primitive types and I can assign int value to long variable without a cast (so they are covariant) then why the code does not compile (covariant return types)? I assume there is something wrong with this sentence or with my understanding of it?

class Class1
{
    long method()
    {
        return 1L;
    }
}
class Class2 extends Class1
{
    public static void main(String[] args)
    {
        int B = 1;
        long A = B; // no cast
    }

//    @Override
//    int method() { return 1; } // does not compile // 'method()' in 'B' clashes with 'method()' in 'A'; attempting to use incompatible return type
}

Solution

  • Your problem has nothing to do with typecasting, but with inheritance. You are trying to override a method and change the return type to an incompatible type. This is not possible, but if it was, it would be very dangerous. The method signature, including the return type is kind of a contract you're not allowed to break. Your calling class is depending on that contract, so if it was possible to override the primitive type, you might end up with the simple problem that your calling class doesn't know the actual return type. But it's usual to work with polymorphism, so at compile time, you have to depend on the contract of the reference type you are using. See the following simple example (doesn't compile due to the last line):

    public class Class1 {
    
        long method() {
            return 3; //int, but cast before the value is returned
        }
    }
    
    class Class2 extends Class1 {
        public static void main(String[] args) {
            int B = 1;
            long A = B; // no cast
        }
    
        int method2() { //renamed to get this compiled
            return 3;
        }
    }
    
    class SimpleClass {
        public static void main(String[] args) {
           int i = 1;
           long j = 2;
           long l = i + j; //this compiles because int is implicitly cast to long
           //int k = i + j; // This doesn't compile because long can't be cast
           //implicitly to int
        }
    }
    
    class Class3 {
    
        void whatIsTheResult() {
            Class1 class1 = new Class1();
            Class1 class12 = new Class2(); //No problem due to  polymorphism, both   
            //objects have the reference type Class1,
        // but actually, class12 is of type Class2
            Class2 class2 = new Class2(); 
    
            long a = class1.method() + class2.method2(); //By contract, method 
            //returns a long, method2 returns an int
            int b = class1.method() + class2.method2(); // Same contract, but long 
            //can't be 'cast down' to int, so doesn't compile
        }
    }