yankee
yankee

Reputation: 40840

Why does inferring of generic type does not work here?

Suppose I have the following classes:

interface MyS<T extends MyT<S, T>, S extends MyS<T, S>> {
}
interface MyT<S extends MyS<T, S>, T extends MyT<S, T>> {
}
public class MySImpl implements MyS<MyTImpl, MySImpl> {
}
public class MyTImpl implements MyT<MySImpl, MyTImpl> {
}

I can build the following testcase which compiles and runs successfully without any warning:

public class STTest {

  @Test
  public void test() throws Exception {
    createInstance(MyTImpl.class);
  }

  public static<T extends MyT<S, T>, S extends MyS<T, S>> void createInstance(
      final Class<? extends MyT<S, T>> beanClass) throws Exception {

    final MyT<S, T> bean = beanClass.newInstance();
  }
}

OK, fine. But I expected that this would have the identical effect:

public class STTest {

  @Test
  public void test() throws Exception {
    createInstance(MyTImpl.class);
  }

  public static<T extends MyT<S, T>, S extends MyS<T, S>> void createInstance(
      final Class<T> beanClass) throws Exception {

    final T bean = beanClass.newInstance();
  }
}

However, this is a compile error:

invalid inferred types for S; inferred type does not conform to declared bound(s) inferred: MySImpl bound(s): MyS

Why is this?

UPDATE:

I noticed that the behaviour is compiler dependent. I used the OpenJDK 1.6 compiler (javac 1.6.0_27) for compiling to code. And it breaks. However:

both work fine on the second example.

However: Is this a bug in the OpenJDK 1.6 compiler or is this an ambiguity in the Java language specification?

Upvotes: 4

Views: 130

Answers (1)

Sebastian Redl
Sebastian Redl

Reputation: 72054

The argument type doesn't mention S in any way in the second example. The compiler is telling you that it therefore can't infer it.

To elaborate. In the first example, you ask for a Class<? extends MyT<S, T>>. In test(), you give it a Class<MyTImpl>. As the compiler checks the bounds, it matches MyT<S, T> against MyTImpl and finds that MyTImpl implements MyT<MySImpl, MyTImpl>, so it can infer MySImpl for S and MyTImpl for T by simply placing those two things alongside.

Then it checks the constraints on S and T for MySImpl and MyTImpl, which succeeds.

In the second example, you ask for a Class<T>. The compiler sees Class<MyTImpl>, infers MyTImpl for T, and is done. Failing to infer S, it errors out.

Constraint checking for T, which could provide the information what S is, doesn't happen.

Upvotes: 3

Related Questions