views:

345

answers:

3

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

A: 

Is your classpath set up so that javac can find Foo?

Robert Grant
+2  A: 

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());
}
Carlos Heuberger
This indeed fixed the problem, although I hoped to avoid this explicit typing with that rather complicate signature of the something() method.I have now simplified it to this, as I need to provide the type anyway:<TV> Maybe<TV> something(final TV value)Thanks for all those hints.
tbh
+3  A: 

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
  }
Itay
The convertion from Maybe<BarExtendsFoo> to a Maybe<Foo> is not what I would expect. The something() method should return `Maybe<TV>`, so `TV` shold be Foo and not BarExtendsFoo. The rewrite is only valid (identical) for javac. Eclipse seams to use the expected return value to infer the (correct?) `TV` type so that something() creates a Maybe<Foo>.
Carlos Heuberger