I'm stuck trying to translate some Java code that uses (bounded) wildcard generics to C#. My problem is, Java seems to allow a generic type to be both covariant and contravariant when used with a wildcard.
[This is a spin-off from a previous question dealing with a simpler case of bounded-wildcards]
Java - works:
class Impl { }
interface IGeneric1<T extends Impl> {
void method1(IGeneric2<?> val);
T method1WithParam(T val);
}
interface IGeneric2<T extends Impl> {
void method2(IGeneric1<?> val);
}
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;
}
}
abstract class Generic<T extends Impl> implements IGeneric1<T>, IGeneric2<T> {
public void method1(IGeneric2<?> val2) {
val2.method2(this);
}
}
C# - doesn't compile...
class Impl { }
interface IGeneric1<T> where T:Impl {
//in Java:
//void method1(IGeneric2<?> val);
void method1<U>(IGeneric2<U> val) where U : Impl; //see this Q for 'why'
// https://stackoverflow.com/a/14277742/11545
T method1WithParam(T to);
}
interface IGeneric2<T>where T:Impl {
void method2<U>(IGeneric1<U> val) where U : Impl;
}
abstract class Generic2<T, TU>: IGeneric2<T> //added new type TU
where T : Impl
where TU : Impl
{
//in Java:
//protected IGeneric1<?> elem;
protected IGeneric1<TU> elem;
//in Java:
//public void method2(IGeneric1<?> val1)
public void method2<U>(IGeneric1<U> val)
where U : TU //using TU as constraint
{
elem = val; //Cannot convert source type 'IGeneric1<U>'
//to target type 'IGeneric1<TU>'
}
public abstract void method1WithParam(T to);
}
abstract class Generic<T> : IGeneric1<T>, IGeneric2<T> where T : Impl
{
//in Java:
//public void method1(IGeneric2<?> val2)
public void method1<U>(IGeneric2<U> val2) where U : Impl
{
val2.method2(this);
}
public abstract T method1WithParam(T to);
public abstract void method2<U>(IGeneric1<U> val) where U : Impl;
public abstract void nonGenericMethod();
}
If I change interface IGeneric1<T> to interface IGeneric1<out T> the above error goes away, but method1WithParam(T) complains about variance:
Parameter must be input-safe. Invalid variance: The type parameter 'T' must be
contravariantly valid on 'IGeneric1<out T>'.
Let me start by saying that is definitely starting to look like a design review is in order. The original Java class aggregates an IGeneric1<?> member, but without knowing its type argument there's no possibility to call method1WithParam on it in a type-safe manner.
This means that elem can be used only to call its method1 member, whose signature does not depend on the type parameter of IGeneric1. It follows that method1 can be broken out into a non-generic interface:
// C# code:
interface INotGeneric1 {
void method1<T>(IGeneric2<T> val) where T : Impl;
}
interface IGeneric1<T> : INotGeneric1 where T : Impl {
T method1WithParam(T to);
}
After this, class Generic2 can aggregate an INotGeneric1 member instead:
abstract class Generic2<T>: IGeneric2<T> where T : Impl
{
protected INotGeneric1 elem;
// It's highly likely that you would want to change the type of val
// to INotGeneric1 as well, there's no obvious reason to require an
// IGeneric1<U>
public void method2<U>(IGeneric1<U> val) where U : Impl
{
elem = val; // this is now OK
}
}
Of course now you cannot call elem.method1WithParam unless you resort to casts or reflection, even though it is known that such a method exists and it is generic with some unknown type X as a type argument. However, that is the same restriction as the Java code has; it's just that the C# compiler will not accept this code while Java will only complain if you do try to call method1WithParam1.
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.
Related
Given the following C# code, how can I translate this to Java?
public class Stop : IComparable<Stop>
{
public int CompareTo(Stop other) { ... }
}
public class Sequence<T> : IEnumerable<T>
where T : IComparable<T>
{
public IEnumerator<T> GetEnumerator() { ... }
IEnumerator IEnumerable.GetEnumerator() { ... }
}
public class Line<T> : Sequence<T>, IComparable<Line<T>>
where T : Stop
{
public int CompareTo(Line<T> other) { ... }
}
I have difficulties translating the definition of class Line to Java. My first attempt would be the following:
public class Line<T extends Stop> extends Sequence<T> implements Comparable<Line<T>> { ... }
However, the compiler reports the following error for extends Sequence<T>:
Error: type argument T is not within bounds of type-variable T
Changing the definition to
public class Line<T extends Comparable<T>> extends Sequence<T> implements Comparable<Line<T>> { ... }
fixes the error, but does not accurately reflect the intent: I want to enforce that all type arguments used with Line must be a sub-type of Stop. Using T extends Comparable<T> would allow arbitrary types that implement the interface.
I do not understand the reason for the error. Is there some way to express this relationship without changing the structure of the types or is this a limitation of Java's generics?
Edit: Visit https://www.onlinegdb.com/S1u9wclnH to see a stripped down version of my attempt.
The problem is your definition of class Sequence.
public class Sequence<T> : IEnumerable<T>
where T : IComparable<T> { ... }
This C# class makes use of the fact that IComparable is contra-variant, so the C# class doesn't require exactly T: IComparable<T>, but is also happy if T is comparable with one of its base classes. Thus the code works even if T is instantiated with a class derived from Stop.
Java does not have declaration-site variance, but use-site variance (wildcards). Your Java Sequence class cannot be instantiated for classes derived from Stop, but your Line class might be. Thus the compiler error.
To fix this, you need to translate C#'s declaration-site variance to Java's wildcards whenever you use Comparable in bounds:
class Sequence<T extends Comparable<? super T>> implements Iterable<T> { ... }
If I have the following method:
public <U extends Number> void doSomething(List<U> l){
}
Then due to type erasure the compiler will make it to doSomething(List<Number> l). Right?
If this is the case, then why it is not possible to declare the following along with this:
public void doSomething(List<?> l){
}
Isn't this second method, type erased to doSomething(List<Object> l)? Why do I get compiler error of same erasure for these 2 methods?
Your thinking is wrong. Erasure leads to both methods having this signature (List argument types being erased):
public void doSomething(List l) {
}
Hence, the collision. What you thought was possible to do is this:
public <U extends Number> void doSomething(U argument) {
}
public <U extends Object> void doSomething(U argument) {
}
In this case, after erasure, the method signatures will become this (after U having been erased)
public void doSomething(Number argument) {
}
public void doSomething(Object argument) {
}
In this case, there is no signature collision.
? is used as wildcard in generics.
It will be erased.
U extends Number tells that upper bound is Number
List<?> doesn't tell what is upper bound.
EDIT: Based on edited question, after compilation, byte code just contains.
public void doSomething(List argument) {
}
public void doSomething(List argument) {
}
Inheritance in generics is little different than what we know as inheritance in java.
1. ? is used as wild card in Generics.
Eg:
Assume Animal is a Class.
ArrayList<? extends Animal> means Any Class's instance which extends Animal
During the Erasure, which is a process in which Compiler removes the Generics in Class and Methods during the Compilation time, to make the Generics code compatible to the one which was written when Generics were not introduced.
So ? and U will be removed during compilation time.. so during runtime it will be absent
Last Thursday someone at work showed me a compile error that I wasn't able to fix in a clean way and it has been bothering me ever since.
The problem is generics related and I've reconstructed a simplified version of the code that generates the compile error. The error occurs in the very last line of code shown below.
I've been looking all over the interwebs but can't seem to find a decent explanation why the Java compiler doesn't accept the code. I guess that if it were to allow the code, it would be possible the create a class cast issue in Bar.operationOnBar(), but I don't see how.
Could someone please enlighten me why this doesn't compile?
public interface Interface {
}
public class Type implements Interface {
}
public class Bar<T> {
public Bar(Class<T> clazz) {
}
public void operationOnBar(Class<T> arg){
}
}
public class Foo {
public <T> Bar<T> bar(Class<T> clazz){
return new Bar<T>(clazz);
}
public static void main(String[] args) {
Class<? extends Interface> extendsInterfaceClazz = Type.class;
new Foo().bar(extendsInterfaceClazz).operationOnBar(Type.class);
}
}
Compile Error on the second line of Foo.main():
The method operationOnBar(Class<capture#1-of ? extends Interface>) in the type Bar<capture#1-of ? extends Interface> is not applicable for the arguments (Class<Type>)
Btw. I've solved it by downcasting Type.class to Class, this way the compiler is unable to see that the generic type of Class is "Type" instead of "? extends Interface".
A little advice: when you are not sure why compiler prohibits some generic-related conversion, replace generic classes in question with List<T>. Then it would be easy to find an example that breaks type safety.
This replacement is correct since currently Java doesn't provide a way to conduct any a priory knowledge about possible behaviours of generic classes (i.e. it lacks a way to specify covariance and contravariance of generic classes in their declarations, as in C# 4 and Scala). Therefore Class<T> and List<T> are equivalent for the compiler with respect to their possible behaviours, and compiler has to prohibit conversions that can cause problems with List<T> for other generic classes as well.
In your case:
public class Bar<T> {
private List<T> l;
public Bar(List<T> l) {
this.l = l;
}
public void operationOnBar(List<T> arg) {
l.addAll(arg);
}
}
List<Type1> l1 = new ArrayList<Type1>();
List<? extends Interface> l2 = l1;
List<Type2> l3 = Arrays.asList(new Type2());
new Foo().bar(l2).operationOnBar(l3);
Type1 t = l1.get(0); // Oops!
You also can change the signature of the method operationOnBar to:
public void operationOnBar(Class<? extends Interface> arg){
You would agree that this shouldn't compile:
1 Class<? extends Interface> clazz = AnotherType.class;
2 new Foo().bar(clazz).operationOnBar(Type.class);
The problem is javac is a little dumb; when compiling line#2, all it knows about variable clazz is its declared type; it forgets the concrete type it was assigned to. So what is assigned to clazz at line#1 doesn't matter, compiler must reject line#2.
We can imagine a smarter compiler that can track the concrete types, then your code can be compiled, as it is obviously safe and correct.
Since that's not the case, sometimes programmers know more about types than the compiler, it is necessary that programmers do casts to convince the compiler.
The general way to deal with these sorts of problems is to introduce a generic argument for the repeated type, which generally means introducing a new generic method (a class would do as well, but isn't necessary).
public static void main(String[] args) {
fn(Type.class);
}
private static <T extends Interface> void fn(Class<T> extendsInterfaceClazz) {
new Foo().bar(extendsInterfaceClazz).operationOnBar(extendsInterfaceClazz);
}
Not really related to the question, but I would suggest using reflection sparingly. It is very, very rarely a good solution.
How can I iterate over a wildcard generic? Basically I would like to inline the following method:
private <T extends Fact> void iterateFacts(FactManager<T> factManager) {
for (T fact : factManager) {
factManager.doSomething(fact);
}
}
If this code is in a separate method as shown, it works because the generic method context allows to define a wildcard type (here T) over which one can iterate. If one tries to inline this method, the method context is gone and one cannot iterate over a wildcard type anymore. Even doing this automatically in Eclipse fails with the following (uncompilable) code:
...
for (FactManager<?> factManager : factManagers) {
...
for ( fact : factManager) {
factManager.doSomething(fact);
}
...
}
...
My question is simply: Is there a way to put some wildcard type one can iterate over, or is this a limitation of generics (meaning it is impossible to do so)?
No. In situation like this, the workaround is to create a helper method.
The JLS has this example http://java.sun.com/docs/books/jls/third_edition/html/conversions.html#5.1.10
public static void reverse(List<?> list) { rev(list);}
private static <T> void rev(List<T> list) { ... }
The issue is, we have a List<?> object. We know it must be a List<X> of some X, and we'd like to write code using X. Internally compiler does convert the wildcard to a type variable X, but Java language does not offer programmers a direct way to access it. But if there's a method accepting List<T>, we can pass the object to the method. Compiler infers that T=X and the call is good.
If there's no type erasure, X can be known at runtime, then Java would definitely give us a way to access X. However as of today since X isn't available at runtime, there's not much point. A purely synthetic way could be provided, which is unlikely to be simpler than the helper method workaround.
Type parameters can only defined on
types (i.e. classes/interfaces),
methods, and
constructors.
You would need a type parameter for a local block, which is not possible.
Yeah, I missed something like this sometimes, too.
But there is not really a problem with having the method non-inlined here - if it presents a performance bottleneck where inlining would help, Hotspot will inline it again (not caring about the type).
Additionally, having a separate method allows giving it a descriptive name.
Just an idea, if you need this often:
interface DoWithFM {
void <T> run(FactManager<T> t);
}
...
for (FactManager<?> factManager : factManagers) {
...
new DoWithFM() { public <T> run(FactManager<T> factManager) {
for (T fact : factManager) {
factManager.doSomething(fact);
}
}.run(factManager);
...
}
...
You can always fall back to Object
for (FactManager<?> factManager : factManagers) {
...
for ( Object fact : factManager) {
factManager.doSomething(fact);
}
...
}
This, of course, is subject to what is the actual declaration of doSomething.
If doSomething is declared as this void doSomething( T fact ), then your recourse here would be to use a raw type and swallow unchecked warnings. If you can guarantee that FactManager can only have homogeneous Facts inserted, then that may be an OK solution.
for (FactManager factManager : factManagers) { // unchecked warning on this line
...
for ( Object fact : factManager) {
factManager.doSomething(fact);
}
...
}
Well, I can think of a way to do it using inner classes, because the inner class shares the type parameter with its enclosing type. Also, even using wildcards you could still process your collections thanks to wildcard capture conversion.
Let me create an example. This code compiles and runs fine. But I cannot be certain if the use of inner classes would be an issue for you.
//as you can see type parameter belongs to the enclosing class
public class FactManager<T> implements Iterable<FactManager<T>.Fact> {
private Collection<Fact> items = new ArrayList<Fact>();
public void doSomething(Fact fact) {
System.out.println(fact.getValue());
}
public void addFact(T value) {
this.items.add(new Fact(value));
}
#Override
public Iterator<Fact> iterator() {
return items.iterator();
}
public class Fact {
//inner class share its enclosing class type parameter
private T value;
public Fact(T value) {
this.value = value;
}
public T getValue() {
return this.value;
}
public void setValue(T value) {
this.value = value;
}
}
public static void main(String[] args) {
List<FactManager<String>> factManagers = new ArrayList<FactManager<String>>();
factManagers.add(new FactManager<String>());
factManagers.get(0).addFact("Obi-wan");
factManagers.get(0).addFact("Skywalker");
for(FactManager<? extends CharSequence> factManager : factManagers){
//process thanks to wildcard capture conversion
procesFactManager(factManager);
}
}
//Wildcard capture conversion can be used to process wildcard-based collections
public static <T> void procesFactManager(FactManager<T> factManager){
for(FactManager<T>.Fact fact : factManager){
factManager.doSomething(fact);
}
}
}
This is more precisely matched to the method you defined (that is, if you can call iterateFacts() with the FactManagers in factManagers, you know that the FactManager contain items that are some subclass of Fact).
for (FactManager<? extends Fact> factManager : factManagers) {
for (Fact fact : factManager) {
factManager.doSomething(fact);
}
}
I would tend to think, however, that you would declare FactManager to be generic for subtypes of Fact (just given the name of the class), e.g.
class FactManager<T extends Fact> implements Iterable<T> {
...
}
The Eclipse refactoring fails because it cannot infer the type of an object contained by FactManager<?>.
Given the interface:
public interface BasedOnOther<T, U extends BasedList<T>> {
public T getOther();
public void staticStatisfied(final U list);
}
The BasedOnOther<T, U extends BasedList<T>> looks very ugly in my use-cases. It is because the T type parameter is already defined in the BasedList<T> part, so the "uglyness" comes from that T needs to be typed twice.
Problem: is it possible to let the Java compiler infer the generic T type from BasedList<T> in a generic class/interface definition?
Ultimately, I'd like to use the interface like:
class X implements BasedOnOther<Y> {
public SomeType getOther() { ... }
public void staticStatisfied(final Y list) { ... }
} // Does not compile, due to invalid parameter count.
Where Y extends BasedList<SomeType>.
Instead:
class X implements BasedOnOther<SomeType, Y> {
public SomeType getOther() { ... }
public void staticStatisfied(final Y list) { ... }
}
Where Y extends BasedList<SomeType>.
Update: ColinD suggested
public interface BasedOnOther<T> {
public T getOther();
public void staticSatisfied(BasedList<T> list);
}
It is impossible to create an implementation such as:
public class X implements BasedOnOther<SomeType> {
public SomeType getOther() { ... }
public void staticStatisfied(MemoryModel list);
} // Does not compile, as it does not implement the interface.
Where MemoryModel extends BasedList<SomeType>, which is needed (as it provides other methods).
It looks as if you don't actually need the type parameter U extends BasedList<T>, if you don't actually need to do anything in the class that requires some specific subclass/implementation of BasedList<T>. The interface could just be:
public interface BasedOnOther<T> {
public T getOther();
public void staticSatisfied(BasedList<T> list);
}
Edit: Based on your update, I don't think there's any way you can do this. I think you'll have to either just go with your original declaration or make some intermediate type that specifies T, like:
public interface BasedOnSomeType<U extends BasedList<SomeType>>
extends BasedOnOther<SomeType, U>
{
}
public class X implements BasedOnSomeType<MemoryModel> { ... }
That seems like kind of a waste though, and I don't really think the original declaration looks that bad.
What about this?
public interface BasedOnOther<T> {
public T getOther();
public <U extends BasedList<T>> void staticStatisfied(final U list);
}
ColinD is almost correct. What you probably want is this:
public interface BasedOnOther<T> {
public T getOther();
public void staticSatisfied(BasedList<? extends T> list);
}
That's because method arguments are covariant but the generics are invariant. Look at this example:
public test() {
Number n1;
Integer n2; //Integer extends Number. Integer is a more-specific type of Number.
n1 = n2; //no problem
n2 = n1; //Type mismatch because the cast might fail.
List<Number> l1;
List<Integer> l2;
List<? extends Number> l3;
l1 = l3; //No problem.
l1 = l2; //Type mismatch because the cast might fail.
}
Why:
Trying to put an Integer where a Number belongs is covariance and it's usually correct for function arguments.
Trying to put a Number where an Integer belongs is the opposite, contravariance, and it's usually correct for function return values. For example, if you defined a function to return a Number, it could return an Integer. If you defined it to return an Integer, however, you couldn't return a Number because that might be a floating-point, for example.
When you're dealing with generics, the compiler can't tell if the generic parameter (in your case, T) is going to be covariant or contravariant. In your code, for example, T is once a return value and once part of an argument. For that reason, generic parameters are invariant by default.
If you want covariance, use <? extends T>. For contravariance, use <? super T>. As a rule of thumb, you probably always want to specify the covariance/contravariance on all public functions. For private functions it doesn't matter as much because you usually already know the types.
This isn't specific to java, other object-oreiented languages have similar issue.
I recently had a very similar problem.
I would suggest, if you are do not need to specifically refer to MemoryModel, i.e. if U extends BasedList<T> is enough, then I would definitely do what Pepe answered.
However, if you must type check for at least two methods that both methods must use MemoryModel specifically and the type inferencing in Pepe's answer is not enough, then the only way to make the use of the clumsy/verbose parameterized constructor more simplistic, is to exploit the generic method parameter inferencing. You would need to create generic static factory methods for each constructor, where the factory methods do the type inferencing (constructors can't type inference in Java).
How to do this is covered in
Effective Java, by Joshua Block; Item 27: Favor generic methods
I also explained this and quoted the solution (with code) here.