Java doesn't allow a type to be both variant and covariant. What you have is an illusion stemming from the fact that while you are declaring IGeneric1<?> elem
in the class Generic2
, you don't use its method T method1WithParam(T val);
; therefore Java don't see any problem with this declaration. It will however flag an error as soon as you will try to use it through elem
.
To illustrate this, the following add a function test()
to the Generic2
class which will try to call the elem.method1WithParam()
function but this leads to a compilator error. The offensive line has been commented out, so you need to re-install it in order to reproduce the error:
abstract class Generic2<T extends Impl> implements IGeneric2<T> {
// !! field using wildcard
protected IGeneric1<?> elem;
public void method2(IGeneric1<?> val1) {
val1.method1(this);
//assignment from wildcard to wildcard
elem = val1;
}
public void test() {
Impl i = new Impl();
// The following line will generate a compiler error:
// Impl i2 = elem.method1WithParam(i); // Error!
}
}
This error from the Java compiler proves that we cannot use a generic type as both covariant and contravariant and this; even if some declaration seems to prove the contrary. With the C# compiler, you don't even have a chance to get that close before getting a compilation error: if you try to declare the interface IGeneric1<T extends Impl>
to be variant with IGeneric1<out T extends Impl>
; you automatically get a compilation error for T method1WithoutParam();
Second, I took a look at the reference .NET equivalent for Java wildcard generics <?> with co- and contra- variance? but I must admit that I don't understand why this can be seen as a solution. Type restriction such as <T extends Impl>
has nothing to do with unbounded wildcard parameterized type (<?>
) or variance (<? extends Impl>
) and I don't see how replacing the seconds with the first could be seen as a general solution. However, on some occasions, if you don't really need to use a wildcard parameterized type (<?>
) or a variance type than yes, you can make this conversion. However, if you don't really use them in your Java code, this one should also be corrected, too.
With Java generics, you can introduce a lot of imprecision but you won't get that chance with the C# compiler. This is especially true considering that in C#, classes and structs are fully reifiable and therefore, do not support variance (both covariance and contravariance). You can use that only for the declaration of an interface and for delegates; if I remember correctly.
Finally, when polymorphism is involved, there is often a bad tendency to use unnecessary generic types; with or without wildcard parameterized types and variance. This often lead to a long and complex code; hard to read and use and even harder to write. I will strongly suggest you to look at all this Java code and see if it's really necessary to have all this stuff instead of a much simpler code with only polymorphism or a combination of polymorphism with generic but without variance or wildcard parameterized type.