Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't my primitive-type-argumented method override the wrapper-type-argumented super class method?

public class WrapperClasses{
    void overloadedMethod(Number N){
        System.out.println("Number Class Type");
    }

    void overloadedMethod(Double D){
        System.out.println("Double Wrapper Class Type");
    }

    void overloadedMethod(Long L){
        System.out.println("Long Wrapper Class Type");
    }

    public static void main(String[] args){
        int i = 21;
        WrapperClasses wr = new WrapperClasses();

        //wr.overloadedMethod(i);
    }
}

class mine extends WrapperClasses{
    void overloadedMethod(int N){
        System.out.println("Integer Class Type");
    }
    public static void main(String[] args){
        int i = 21;
        WrapperClasses wr = new mine();

        wr.overloadedMethod(i);
    }
}

This prints Number Class Type.

I understand the rules of wrapper class method overloading:

  1. If you are passing a primitive data type as an argument to the method call, the compiler first checks for a method definition which takes the same data type as an argument.
  2. If such a method does not exist, then it checks for a method definition which takes a larger-sized primitive data type than the passed data type. I.e., it tries to perform auto-widening conversion of the passed data type.
  3. If auto-widening conversion is not possible, then it checks for a method definition which takes the corresponding wrapper class type as an argument. I.e., it tries to perform auto-boxing conversion.
  4. If such a method does not exist, then it checks for a method which takes the super class type (Number or Object type) as an argument.
  5. If such a method also does not exist, then the compiler gives a compile-time error.

According to rule 1, it should print Integer Class Type. What am I missing here?

like image 663
amarnath harish Avatar asked Sep 07 '18 07:09

amarnath harish


3 Answers

At a language spec level, it is because methods with parameters which differ as being primitive and wrapped primitive types are not considered as override-equivalent. (A fancy way of saying "they just don't because the language spec says so").

But logically, they shouldn't either, at least in the case of an int parameter in a subclass "overriding" a wrapped parameter in a superclass.

By Liskov's Substitution Principle, methods in subclasses must accept at least all the parameters accepted by the method in the superclass.

If the superclass method accepts the wrapped class, it can accept null. If the subclass method were allowed only to accept int, it could not accept null, so it would not be substitutable.

like image 63
Andy Turner Avatar answered Nov 15 '22 16:11

Andy Turner


Method overload resolution is determined at compile time, based on the compile time type of the variable holding the reference to the instance for which you are calling the method.

void overloadedMethod(int N) is only defined in the sub-class mine. Therefore, when you call a method on a reference whose type is the base class WrapperClasses, only the methods of the base class can be considered for overload resolution.

The int argument that you pass to the method doesn't match any of the 3 methods of the base class, but after it is boxed to Integer, it matches the void overloadedMethod(Number N) method.

If you change your code to

    int i = 21;
    mine wr = new mine();

    wr.overloadedMethod(i);

it will execute void overloadedMethod(int N).

like image 38
Eran Avatar answered Nov 15 '22 16:11

Eran


Polymorphism is performed by the JVM at runtime. For this to work, the two methods must have the same runtime signature.

In the case of covariant return types, the compiler generates bridge methods to allow this to happen, however, the Java language Spec doesn't require such a bridge method for wrappers vs primitives.

There is a number of reasons this might be, however the most likely is backward compatibility. Java 1.0 didn't do this, and even though autoboxing was added more than a decade later, this wasn't allowed to break older code. i.e. wrapper and primitives overloaded each other, not overrode, so they can't now.

like image 30
Peter Lawrey Avatar answered Nov 15 '22 14:11

Peter Lawrey