9

I have some Guice binding code using generics that compiles and functions fine from Eclipse's compiler, but not from the Java (command-line) compiler. I upgraded to the latest (1.7.0_01) Java SDK but still get the following error.

[error] ...\BindCategorySelectorActivity.java:42: error: inconvertible types
[error]                                 (Class<? extends ListAdapterDataProvider<Row<? extends DatabaseItem>>>) CategoryDataProvider.class);
[error]                                                                                                                             ^
[error]   required: Class<? extends ListAdapterDataProvider<Row<? extends DatabaseItem>>>
[error]   found:    Class<CategoryDataProvider>
[error] 1 error
[error] {file:/.../compile:compile: javac returned nonzero exit code

Relevant code:

public interface Category extends DatabaseItem {}
public class CategoryDataProvider implements 
 ListAdapterDataProvider<Row<Category>> {}
public class BindListViewHandlerWithSpecificProvider extends AbstractModule {
    public BindListViewHandlerWithSpecificProvider(
     Class<? extends ListAdapterDataProvider<Row<? extends DatabaseItem>>>
      dataProviderClass) {}
}

@SuppressWarnings("unchecked")
// Error happens here:
final BindListViewHandlerWithSpecificProvider 
 bindListViewHandlerWithSpecificProvider = 
  new BindListViewHandlerWithSpecificProvider(
   (Class<? extends ListAdapterDataProvider<Row<? extends DatabaseItem>>>)
    CategoryDataProvider.class);
Jeff Axelrod
  • 27,676
  • 31
  • 147
  • 246

2 Answers2

6

Do yourself a favor and do an upcast followed by a downcast:

Class<...> foo = (Class<...>)(Object)MyClass.class;

The issue is that CDP.class is of type Class<CDP>, CDP being a raw type. While a parameterized type C<T1,...,Tn> is the subtype of the raw type C (§4.10.2), the inverse is not true: C is not a subtype of C<T1,...,Tn>. This only appears to be true due to unchecked conversion (§5.1.9). This is causing your issue: You expect CDP to "extend" (as in the upper bound of Class<? extends ...>) LADP<Row<? extends DI>>. This is not the case because type argument containment (§4.5.1.1) is defined over subtyping and does not consider unchecked conversion.

(Or to cut to the chase: javac has got this one right.)

Ben Schulz
  • 6,101
  • 1
  • 19
  • 15
  • Well, it managed to compile, but I'm not sure if adding the extra cast to Object just masked the problem. Guice crashes with a null pointer exception when trying to bind. The line its reporting is accessing the `bindListViewHandlerWithSpecificProvider`. And remember, this identical code compiles and executes without error when built within Eclipse. – Jeff Axelrod Nov 14 '11 at 22:01
  • 1
    I will admit that's curious behavior. Doing the extra cast to Object does nothing beyond tricking javac out of `sideCast`ing. You _should_ end up with effectively the same program. – Ben Schulz Nov 15 '11 at 07:47
  • Are you familiar with Guice's added support for generics and if there would be a better mechanism to passing parameterized types? Any idea if Scala provides better support for passing parameterized types? – Jeff Axelrod Nov 15 '11 at 13:12
  • 1
    I have used Guice a long while back and IIRC it uses [Super-Type-Tokens](http://gafter.blogspot.com/2006/12/super-type-tokens.html); if and how they can help you here I really can't say. Scala offers [manifests](http://www.scala-blogs.org/2008/10/manifests-reified-types.html) to much the same effect, except that the compiler will infer them for you. It's unlikely this will help you here though.. – Ben Schulz Nov 15 '11 at 15:07
1

I know the answer is working and accepted but I believe the downcast is not the perfect solution. Also my code cleanup remove the obsolete down cast...

1) the reason eclipse and command line does not yield the same problem is because of eclipse settings. Go to preferences - java - compiler - Errors/Warnings and set Generic types (unchecked generic type operation) to warning. Then you will detect the same problem if you remove the @SuppressWarnings("unchecked")

2) I had a similar issue and a friend show me another solution. To fix the code properly (without the down cast) just change the CategoryDataProvider.class to this:

new CategoryDataProvider<Row<DatabaseItem>>().getClass()

Then put back the @SuppressWarnings("unchecked")

Martin P.
  • 654
  • 8
  • 18
  • This method could work only if the class (in this case, CategoryDataProvider) has a default constructor. In more complicated cases, this method might require you to do a great deal of preparations and initializations, just to recover the class. – Erel Segal-Halevi Feb 24 '14 at 08:13