3

I have a class which has a self referential generic parameter and a parameter which is of the same super class. The static function has identical bounds as the class.

public class Bar<T extends Bar<T, C>, C extends Bar<C, ?>> {

    Bar() {
        foo((T) null);
        foo((C) null);//compile error
    }

    static <S_T extends Bar<S_T, S_C>, S_C extends Bar<S_C, ?>> void foo(S_T t) {
    }
}

This gives the following error.

Bound mismatch: The generic method foo(S_T) of type Bar<T,C> is not applicable for the arguments (C). The inferred type C is not a valid substitute for the bounded parameter <S_T extends Bar<S_T,S_C>>

I can't figure out why C can't be passed in to foo() since C is Bar<C,?> and the wildcard is a Bar because of the second parameter in the declaration says it extends Bar.

I know this is probably a bad idea and produces code which is hard to understand but I really wanna know why this doesn't compile.

Judge Mental
  • 5,209
  • 17
  • 22
dege
  • 73
  • 1
  • 8
  • possibly related to my question http://stackoverflow.com/questions/9937422/a-bad-interaction-between-self-referential-types-and-bounded-wildcards – Judge Mental Jun 11 '12 at 23:02
  • @JudgeMental somewhat similar; but from what I gathered from your question it's the class hierarchy that failed to compile, while my class hierarchy compiles but fails on the argument constraint for the function. – dege Jun 11 '12 at 23:44

2 Answers2

2

The short answer is that Java type inference is actually pretty lame.

Java is not performing any intelligent inference on the wildcard ? in the declaration of Bar to infer that it is logically bounded by Bar< ?, ? > (nor that the ?s in that bound are themselves bounded, and so on). Once you put ? by itself, that's all Java knows. Although nothing is stopping you from putting that bound in on the wildcard in the declaration of Bar, even that does not help Java; Java won't ever assume that two separate ?s refer to the same type, even if deeper analysis would imply that they have to be. In other words, the compile error persists even with this code:

public class Bar<T extends Bar<T, C>, C extends Bar<C, ? extends Bar<?, ? extends Bar<?, ?>>>> {

    Bar() {
        foo((T) null);
        foo((C) null);//compile error
    }

    static <S_T extends Bar<S_T, S_C>, S_C extends Bar<S_C, ? extends Bar<?, ? extends Bar<?, ?>>>> void foo(S_T t) {
    }
}
Judge Mental
  • 5,209
  • 17
  • 22
  • Yes, I started to suspect that java couldn't detect the connection between the wildcard bounds. I started experimenting with supplying a class: `Pie extends Bar` instead of the wildcard. Then the function would have `, S_C extends Bar` and one could still have methods on Bar which would return S_C....But I might be completly off target now (almost 3am here) – dege Jun 12 '12 at 00:41
  • what would be wrong with `Bar< S_T, S_C >` as the argument type to `foo`? The only information you can use about `S_T` in `foo` is that it is a `Bar< S_T, S_C >` anyway. Plus that makes `foo` (theoretically, at least) more general. – Judge Mental Jun 13 '12 at 19:14
  • What would be the generic parameters for the function? Do you mean just defining S_T and S_C without any bounds, because then Bar in the argument won't match the bounds for the Bar class – dege Jun 13 '12 at 20:31
  • Right, that doesn't help much. It occurs to me that if you want to call `foo( ( C )null )`, you don't care about the second parameter to `Bar` in `foo` either, which means why not just `static > void foo( S_T t ) {}` ? – Judge Mental Jun 13 '12 at 20:58
  • Well that kinda works, depending of what one want to do, since you can't call `new Bar()` if you have a wildcard instead of S_C. – dege Jun 13 '12 at 21:31
0

I'm not a crack with generics, but I think the problem is that you declare the type of foo() to be

<S_T extends Bar<S_T,S_C> > void foo(S_T)

and then call it in two different contexts that require a different static type for foo().

In the fist context S_T has type T and in the second context it has type C. But T and C are declared as Bar<T,?> and Bar<C,?>, which are incompatible types statically. I would guess that the compiler figures out the type of foo() in the first call and then assumes correctly, that the type must remain the same throughout, which it doesn't.

Jochen
  • 2,277
  • 15
  • 22
  • Switching the order of the two foo() calls still gives an error on the line with the C. – dege Jun 11 '12 at 18:44
  • OK, try changing the order of C and T in the class declaration, ie. do Bar, T extends Bar. If the error flips then, it means that the type bindings also depend on the order of the types in the class declaration. But in either case, C and T have different incompatible types and you can't pass both into foo(). – Jochen Jun 11 '12 at 18:48
  • I tried the change you suggested and the error changed to T but I think that's simply because all it did was rename the parameters, not change any meaning. I tried a couple of different ways of reording the parameters and the error stays on the parameter which has the wildcard. Also I think that although C and T are incompatible with eachother both should be compatible with the static function since AFAIK its generic parameters are based on the input it get each call. – dege Jun 11 '12 at 19:07