11

The following code works when compiled with sourceCompatibility=1.7 or 1.6, but fails after switching to 1.8:

public class Java8Wat {
  interface Parcelable {
  }

  static class Bundle implements Parcelable {
    public void put(Parcelable parcelable) {
    }

    public void put(Serializable serializable) {
    }

    public <T extends Parcelable> T getParcelable() {
      return null;
    }
  }

  static {
    Bundle inBundle = new Bundle();
    Bundle outBundle = new Bundle();

    outBundle.put(inBundle.getParcelable());
  }
}

Compilation output:

Java8Wat.java:23: error: reference to put is ambiguous
        outBundle.put(inBundle.getParcelable());
             ^
both method put(Parcelable) in Bundle and method put(Serializable) in Bundle match

Here's the repo with failing code: https://github.com/chalup/java8-wat. Just invoke ./gradlew clean build from project directory.

I skimmed through JLS for Java 8, but I haven't found anything relevant.

Additional observation: the code compiles if I change the getParcelable() signature to:

public Parcelable getParcelable()

Why does java compiler considers put(Serializable) to be potentially applicable method for outBundle.put(inBundle.getParcelable()) call and what changes should be made to Parcelable/Bundle class? Bonus question: why does this error happens only on Java 8 and not on Java 7?

chalup
  • 8,358
  • 3
  • 33
  • 38

2 Answers2

3

I would suggest that this is due to changes in how inference is done in java 8. And to the fact that Parcelable is an interface. Because of this, the infered return type of getParcelable results in an ambiguous call because the infered return type can be applied to both methods.

I would mainly refer to this other question for a clearer explanation : Why can this generic method with a bound return any type?

As of to the real understanding of how the inference works in this specific case and why it is different between java 7 and 8, this would require a deeper study of the inference part of JLS.

Community
  • 1
  • 1
benzonico
  • 10,635
  • 5
  • 42
  • 50
  • 1
    It works in Java 7 because Java 7 only inferred type arguments based on a return target if it was subject to assignment conversion. Otherwise the upper bound gets inferred. In other words, the fact that one call is inside the other like `f(g());` is why there is a difference. In Java 7, `put(Serializable)` can't be applicable this way. – Radiodef Apr 17 '15 at 08:19
1

As the Message says the reference to put is ambiguous.

You have to explizity cast the value so that the compiler knows wich method you would like to use:

outBundle.put((Parcelable)Bundle.getParcelable());

or

outBundle.put((Serializable)Bundle.getParcelable());
Jens
  • 67,715
  • 15
  • 98
  • 113
  • That's a workaround, but what I want to know is why the compiler assume that the return type of `getParcelable` is `Serializable`, which leads to ambiguity. – chalup Apr 17 '15 at 07:01
  • 1
    but why's there difference between java 8 and earlier ones? – eis Apr 17 '15 at 07:02
  • 2
    This answer only addresses one part of the two (or three) part question. – FThompson Apr 17 '15 at 07:10
  • If we're talking about "how do I made this code compile?" workaround, obviously the cleaner solution would be adding explicit type argument to getParcelable call. – chalup Apr 17 '15 at 07:15
  • 2
    @Jens hasn't the subject of the question the same all the time? This answer does not address that. – eis Apr 17 '15 at 07:19