Question

Calling this method:

public static @Nonnull <TV, TG extends TV> Maybe<TV> something(final @Nonnull TG value) {
    return new Maybe<TV>(value);
}

like this:

public @Nonnull Maybe<Foo> visit() {
    return Maybe.something(new BarExtendsFoo());
}

compiles just fine in Eclipse, but javac gives an "incompatable types" warning:

found   : BarExtendsFoo

required: Foo

Was it helpful?

Solution

I do not understand why javac did not infer the correct type,
but you can help the compiler by supplying the types as in

public @Nonnull Maybe<Foo> visit() {
    return Maybe.<Foo, BarExtendsFoo>something(new BarExtendsFoo());
}

OTHER TIPS

There are obviously some differences between javac and Eclipse. However, the main point here is that javac is correct in emitting the error. Ultimately, your code converts a Maybe<BarExtendsFoo> to a Maybe<Foo> which is risky.

Here's a rewrite of the visit() method:

  public static <TV, TG extends TV> Maybe<TV> something(final TG value) {
     return new Maybe<TV>(value);
  }

  public static class Foo { }

  public static class BarExtendsFoo extends Foo { }

  public Maybe<Foo> visit() {
     Maybe<BarExtendsFoo> maybeBar = something(new BarExtendsFoo());
     Maybe<Foo> maybeFoo = maybeBar;  // <-- Compiler error here

     return maybeFoo;      
  }

This rewrite is practically identical to your code but it explicitly shows the assignment you're trying to make from Maybe<BarExtendsFoo> to Maybe<Foo>. This is risky. Indeed my Eclipse compiler issues an error on the assignment line. Here's a piece of code that exploits this risk to store an Integer inside a Maybe<String> object:

  public static void bomb() {
     Maybe<String> maybeString = new Maybe<String>("");

     // Use casts to make the compiler OK the assignment
     Maybe<Object> maybeObject = (Maybe<Object>) ((Object) maybeString); 
     maybeObject.set(new Integer(5));

     String s = maybeString.get(); // Runtime error (classCastException):
                                   //   java.lang.Integer incompatible with  
                                   //   java.lang.String
  }

Two comments:

a. As you mentioned in one of your comments, The TG type parameter in something()'s signature is not necessary at all, as there is nothing specific to the subclass TG that you're doing in the method.

b. The easiest soluion to this would be to help the compiler to understand which type you're using by explicitly assigning the newly created object to a variable (which is usually good practice anyway). It's now clearer to both the compiler and human readers how you'd like to call the method:

public @Nonnull Maybe<Foo> visit() {
    final Foo bar = new BarExtendsFoo();
    return Maybe.something(bar);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top