11

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

Clashsoft
  • 11,553
  • 5
  • 40
  • 79

3 Answers3

6

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.

Paul Boddington
  • 37,127
  • 10
  • 65
  • 116
  • I don't think this feature would be pointless by any means: A concrete example would be a `BitSet` class that extends `Set`. This way, you can get the box-free high-performance of the BitSet and still use Set functionality when something expects a generic Set. – Clashsoft Sep 06 '15 at 11:06
  • @Clashsoft I completely agree with that. I have argued for something similar myself (see the question "SparseArray vs HashMap") where I argue that android's `SparseArray` should have implemented `Map`. That way it can be a treated as a `Map` when required yet deal only with primitive keys. That would have been great, but it's just that you can do it already by overloading the methods - no need for new rules. – Paul Boddington Sep 06 '15 at 12:19
  • But you can only overload parameter types, not return types. That makes is impossible to override Map in your scenario, which means you'd need an additional wrapper that implements the Map interface and delegates to a SpareArray instance. – Clashsoft Sep 06 '15 at 15:18
  • @Clashsoft You're right, I didn't mean overload in the case of changing the return type - the method has to have a different name. You can definitely do it with `SparseArray` using inheritance rather than composition as I've done it. `public final class SparseMap extends SparseArray implements Map { @Override public T get(Object i) { return get((int) (Integer) i); } // etc }`. The main problem I have with all this is that you have to throw out the rule of always programming to the interface. I'm looking forward to Java 9 (or is it 10?) when we can have a true `Foo`. – Paul Boddington Sep 06 '15 at 15:31
4

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().

Mifeet
  • 12,949
  • 5
  • 60
  • 108
  • 1
    Indeed it might not be obvious that the `int moo()` overrides `T moo()` (with `T = Integer`). But if you could write `Foo`, things would be a bit more obvious. Guess we'll have to wait for Java 10 then :/ – Clashsoft Sep 06 '15 at 11:13
2

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

ControlAltDel
  • 33,923
  • 10
  • 53
  • 80
  • 2
    Where do you need different stack sizes in my example? If you call the overridden method directly, you get an `int` from the implementation, otherwise the bridge method gets called and handles the boxing for you. – Clashsoft Sep 05 '15 at 23:12