Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is it not possible use primitive types with polymorphic return types?

Consider the following two classes:

public interface Foo<T>
{
    public T moo();
}

public class IntFoo implements Foo<Integer>
{
    public int moo()
    {
        return 0;
    }
}

This code will produce an error at publicintmoo, saying that int is incompatible with the overridden method's return type Integer. Strictly speaking, this is true, since int does not directly equal Integer. However, we all know that they can be implicitly converted to each other using auto(un)boxing. What is less know is the fact that the compiler generates a bridge method in this example:

public class IntFoo implements Foo<Integer>
{
    public <synthetic> <bridge> Object moo()
    {
        return this.moo(); // upcast
    }

    public Integer moo() {
        return 0;
    }
}

This has to be done because the JVM differentiates between return types when resolving methods, and since the erased return type of Foo.moo is Object, the compiler generated a bridge method with the same signature as the method.

I am wondering why this wouldn't work with primitive polymorphic return types as well:

public class IntFoo implements Foo<Integer>
{
    public <synthetic> <bridge> Object moo()
    {
        return Integer.valueOf(this.moo());
    }

    public int moo()
    {
        return 0;
    }
}

There doesn't seem to be any reason not to have this feature:

IntFoo intFoo = new IntFoo();
Foo<Integer> foo = intFoo;
Integer i = foo.moo(); // calls the synthetic method, which boxes the result of the actual implementation

In fact, this screenshot of a REPL session shows that I was even able to implement this in my custom programming language (which compiles down to Java bytecode):

REPL session

like image 660
Clashsoft Avatar asked Sep 05 '15 22:09

Clashsoft


3 Answers

As always with these questions, the answer is that you'd have to ask the language designers. I can't see any reason why this couldn't be done. However in my opinion this feature would be fairly pointless. As you point out in the question it's only when moo is invoked on a variable of static type IntFoo that a primitive would get returned; on a variable of type Foo<Integer>, an Integer would get returned anyway. So you can achieve essentially the same thing by doing this.

public class IntFoo implements Foo<Integer> {

    @Override
    public Integer moo() { return mooAsInt(); }

    public int mooAsInt() { return 0; }
}

Personally I think this is better because it's much more obvious when boxing does / does not take place. In your proposal, moo() could return an int or an Integer depending on the static type of the variable, which would be extremely confusing.

like image 192
Paul Boddington Avatar answered Oct 17 '22 22:10

Paul Boddington


In my humble opinion, the reason is purely syntactic. As you have demonstrated in the question, it is possible to generate a method returning int and a method implementing the Foo interface method returning Object in the bytecode.

However, if you look at the problem from the Java syntax point of view (i.e. not bytecode),

public class IntFoo implements Foo<Integer> {
    public int moo() { return 0; }
}

is supposed to override a method Integer moo() which it doesn't. As you said yourself, Java distinguishes return types and int moo() is not the same as Integer moo().

like image 27
Mifeet Avatar answered Oct 17 '22 21:10

Mifeet


The problem with primitives is that they need different amounts of space on the stack... As opposed to object references which all take the same amount of space on the stack. And you can't have a variable sized stackframe, because otherwise at runtime the stack wouldn't know where to go back to on method exit

like image 2
ControlAltDel Avatar answered Oct 17 '22 23:10

ControlAltDel