Java generics with wildcard compile in Eclipse, but not in javac - java

As a follow up to Java generics compile in Eclipse, but not in javac, I post another snippet which compiles and runs fine in Eclipse, but raises a compilation error in javac. (This prevents the project the snippet is extracted from, from being build with Maven.)
The self-contained snippet:
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
public class Main {
public static void main(String[] args) {
Set<Foo<?>> setOfFoos = new HashSet<Foo<?>>();
List<Foo<?>> sortedListOfFoos = asSortedList(setOfFoos);
}
public static <T extends Comparable<T>> List<T> asSortedList(Collection<T> c) {
List<T> list = new ArrayList<T>(c);
java.util.Collections.sort(list);
return list;
}
public static class Foo<T> implements Comparable<Foo<T>> {
#Override
public int compareTo(Foo<T> o) {
return 0;
}
}
}
Compilation in javac returns:
Main.java:11: <T>asSortedList(java.util.Collection<T>) in Main cannot be applied to (java.util.Set<Main.Foo<?>>)
List<Foo<?>> sortedListOfFoos = asSortedList(setOfFoos);
^
On substitution of Foo<?> with Foo<String> the above snippet will compile in javac, which means the problem is related to the used wildcard. As the Eclipse compiler is supposed to be more tolerant, is it possible the snippet is no valid Java?
(I use javac 1.6.0_37 and Eclipse Indigo with compiler compliance level 1.6)
(EDIT1: Included another example which got removed in EDIT2.)
EDIT2: Hinted by irreputable, that comparing Foo<A> and Foo<B> may be conceptually wrong, and inspired by the answer of seh, a working asSortedFooList can be written as follows:
public static <T extends Foo<?>> List<T> asSortedFooList(Collection<T> c) {
List<T> list = new ArrayList<T>(c);
java.util.Collections.sort(list);
return list;
}
(Simple substitution of Comparable<T> with Foo<?> in the method definition above.)
So it seems to be safe for javac and imho conceptually right to compare any Foo<A> and Foo<B>. But it is still not possible to write a generic method asSortedList which returns a sorted list representation for a generic collection, if its type argument is parametrized with a wildcard. I tried to "trick" javac by substituting Foo<?> by S extends Comparable<S> in asSortedFooList, but this didn't work.
EDIT3: Later Rafaelle pointed out, that there is a flaw in the design, since implementing Comparable<Foo<T>> is not necessary, and implementing Comparable<Foo<?>> provides the same functionality, solving the initial problem by refined design.
(The initial reason and benefit was, that a Foo<T> may not care in some purposes about its concrete type but still use an instance of a concrete type T, it is instantiated with, for other purposes. That instance does not have to be used for determining the order among other Foos, as it may be used in other parts of the API.
Concrete example: Assume each Foo is instantiated with a different type argument for T. Every instance of Foo<T> has an incrementing id of type int which is used in the implementation of the compareTo-method. We can now sort a list of these differently typed Foo and don't care about the concrete type T (expressing it with Foo<?>) and still have an instance of a concrete type T accessible for later processing.)

To me this is another javac bug. When you try to send a Collection<Foo<?>> to a method with the signature:
public static <T extends Comparable<T>> List<T> asSortedList(Collection<T> c)
the compiler notes that the formal parameter T has an upper bound, so checks if the constrained is honored by the caller. The type argument is a (wildcard) instantiation of the parameterized type Foo<T>, so the test will pass if Foo<?> is-a Comparable<Foo<?>>. Based upon the generic definition:
class Foo<T> implements Comparable<Foo<T>>
I'd say that it's true, so again Eclipse is right and javac has a bug. This Angelika Langer's entry is never linked enough. Also see the relevant JLS.
You asked if it is type-safe or not. My answer is that it is type safe, and it shows you have a flaw in your design. Consider your fictitious implementation of the Comparable<T> interface, where I added two more fields:
public static class Foo<T> implements Comparable<Foo<T>> {
private T pState;
private String state;
#Override
public int compareTo(Foo<T> other) {
return 0;
}
}
You always return 0, so the problem is not spotted. But when you try to make it useful, you have two options:
Comparing on the String field
Comparing on the T member
The String field is always a String, so you don't really benefit from the type variable T. On the other hand, T has no other type information available, so in compareTo() you can only deal with a plain object, and again the type parameter is useless. You can achieve the same exact functionality by implementing Comparable<Foo<?>>

In this case, javac is correct. Conceptually, your code can't work, since the set may contain Foo<A> and Foo<B>, which can't be compared to each other.
You probably want the set to be a Set<Foo<X>> for some type variable X; unfortunately we can't introduce type variable inside method body; only in method signature
<X> void test(){
Set<Foo<X>> setOfFoos = new HashSet<Foo<X>>();
List<Foo<X>> sortedListOfFoos = asSortedList(setOfFoos);
}
You may make it work by something like
<T extends Comparable<? super T>> List<T> asSortedList(Collection<T> c)
class Foo<T> implements Comparable<Foo<?>>

I don't know if this is a question, but here is a (not very nice) answer:
If you sacrifice some type safety you can write
#SuppressWarnings({ "unchecked", "rawtypes" })
public static <T extends Comparable> List<T> asSortedList(Collection<T> c) {
List<T> list = new ArrayList<T>(c);
java.util.Collections.sort(list);
return list;
}
And it works in both eclipse and javac. The only risk that I'm aware of is that if someone creates a class Foo extends Comparable<Bazz> you won't detect that in compile time.
But if someone creates Foo extends Comparable<Bazz>, just kill him/her.

I found a solution that compiles with javac, though I am not happy that I am unable to explain exactly why it works. It requires introducing an intermediary function:
public final class Main {
public static class Foo<T> implements Comparable<Foo<T>> {
#Override
public int compareTo(Foo<T> o) {
return 0;
}
}
public static <T extends Comparable<? super T>>
List<T> asSortedList(Collection<T> c) {
final List<T> list = new ArrayList<T>(c);
java.util.Collections.sort(list);
return list;
}
private static <T extends Foo<?>> List<T> asSortedFooList(Collection<T> c) {
return asSortedList(c);
}
public static void main(String[] args) {
final Set<Foo<?>> setOfFoos = new HashSet<Foo<?>>();
final List<Foo<?>> listOfFoos = asSortedFooList(setOfFoos);
}
}
I think that this works by virtue of taking the wildcard resolution step-by-step; asSortedFooList() captures one type known to be a Foo, irrespective of Foo's type parameter. With that type parameter bound in asSortedFooList(), we can then call on your original asSortedList() (well, with one modification—note the lower bound on the type parameter for Comparable) requiring binding Foo as a type descended from Comparable.
Again, that's a weak, haphazard explanation. My main point in answering here is just to provide one more way to get to your destination.

If you can replace your wildcard usage with an exact type (which may be a super-type) your code will work. Replace
List<Foo<?>> sortedListOfFoos = asSortedList(setOfFoos);
with
List<Foo<String>> sortedListOfFoos = Main.<Foo<String>>asSortedList(setOfFoos);

Related

Discrepancy between Eclipse compiler and javac - Enums, interfaces, and generics

The following code compiles (and runs tests as expected) in Eclipse:
import java.util.EnumSet;
public class EnumTest {
static enum Cloneables implements Cloneable {
One, Two, Three;
}
public <T extends Cloneable> T getOne(Class enumType) {
EnumSet<? extends T> set = EnumSet.allOf(enumType);
return set.iterator().next();
}
}
However, compiling with either javac (JDK 7) directly or via Maven fails with the following error:
type argument ? extends T is not within bounds of type-variable E
To be honest, the complexity of enums + interfaces + type-parameters (generics) all at play at once threw me off as I was writing the code, but I thought I had finally gotten it right.
The goal is to write calling code like this:
Cloneable something = enumTest.getOne(Cloneables.class);
For example, in Eclipse the following test compiles and passes:
#Test
public void testGetFirst() {
assertSame(Cloneables.One, getOne(Cloneables.class));
}
Any clues about which is "correct," Eclipse or javac, are appreciated.
Also appreciated is any advice about alternate ways to implement the idea: take a class as a method param that can be used in EnumSet.allOf() and that also determines the type of Enum objects in the EnumSet
By the way, don't bother critiquing the purpose of this method; I've reduced it down from more useful/meaningful code. I'm not interested in discussing the merits of "finding the first element from an enum type" - that's not the point of this question.
You need to make sure that T is an enum type, or it won't meet the constraints for EnumSet:
public <T extends Enum<T> & Cloneable> T getOne(Class enumType)
Also, you don't need the wildcard in your EnumSet, and you shouldn't use the raw Class type:
public <T extends Enum<T> & Cloneable> T getOne(Class<T> enumType) {
EnumSet<T> set = EnumSet.allOf(enumType);
return set.iterator().next();
}

Java generics compilation error - The method method(Class<capture#1-of ? extends Interface>) in the type <type> is not applicable for the arguments

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.

Java generic programming with unknown generic type of interface

I'm using several interfaces with generics types. While combining it together I have some problems when I have to use them from a part of the code that is unaware of the concrete type of the generic parameter.
Suppose I have the following interface:
public interface MyObjectInterface<T extends Number> {}
The object implementing that interfaceare stored in a generic collection with the same generic type:
public interface MyCollectioninterface<T extends Number> {
public void updateObject(MyObjectInterface<T> o);
}
Concrete instances of MyCollectionInterface hold several MyObjectInterface of the same generic parameter:
public class ConcreteCollection<T extends Number> implements
MyCollectionInterface<T> {
List<MyObjectInterface<T>> list;
public void updateObject(MyObjectInterface<T> o){}
}
Now, I have several questions on how to use these generic interfaces from a client class that is
(and must be) unaware of the concrete type of generics.
Suppose I have the following class:
public class ClientClass{
private MyCollectionInterface<?> collection; //1st possibility
private MyCollectionInterface collection; //2nd possibility
public ClientClass(MyCollectionInterface<?> collection){
this.collection = collection;
}
public void foo(MyObjectInterface<?> o){
this.collection.updateObject(o); //this doesn't compile
}
public void foo(MyObjectInterface<? extends Number> o){
this.collection.updateObject(o); //this doesn't compile either
}
public void bar(MyObjectInterface o){
MyObject b = o; //warning
this.collection.updateObject(o); //this compile but with warnings
}
}
First Question :
Considered the fact that ClientClass doesn't care of which concrete type extending Number is the collection, should I declare collection with or without "?" ? If I use the second version I get the following warning:
MyCollectionInterface is a raw type. References to generic type
LatticeInterface should be parameterized
Second Question :
Why method foo doesn't compile?
Third question:
It seems that I need to use bar signature to call updateObject method. Anyway this solution produce a warning while trying to assign the MyObjectInterface parameter, like in the first question. Can I remove this warning?
Last questions:
Am I doing something weird with this generic interfaces and I should refactor my code?
Do I really have to care about all these warnings?
How can I use safety a generic interface from a class where I don't know its concrete type?
Ok, I played a bit with your code and reached a conclusion.
The problem is that your ConcreteCollection (and its interface MyCollectionInterface) declare the method updateObject as receiving an argument of type MyObjectInterface<T> (where T extends Number) - note that the type is a concrete one (not a wildcard).
Now, in your client class you are receiving a collection and storing it as MyCollectionInterface<?> but the instance that is passed to ClientClass' constructor will be of a concrete type, for instance:
new ClientClass(new ConcreteCollection<Integer>());
This means that the method updateObject of that instance would only accept an argument of type MyCollectionInterface<Integer>.
Then, in method foo you are trying to pass a MyObjectInterface<?> to updateObject, but since the compiler doesn't know which generic type your collection accepts (it could be Integer like in my example but it could also be Double or any other type that extends Number), it won't allow any object to be passed.
Long story short, if you declare your reference as MyCollectionInterface<?> you won't be able to call updateObject on it. So you have two choices:
1) Pick a concrete type and stick with it:
private MyCollectionInterface<Number> collection;
public ClientClass(MyCollectionInterface<Number> collection){
this.collection = collection;
}
public void foo(MyObjectInterface<Number> o){
this.collection.updateObject(o); //compiles
}
But then you are limiting the collections you can receive in your constructor (which may not be a bad idea), or:
2) Modify your interface to accept a wildcard type:
public interface MyCollectionInterface<T extends Number> {
public void updateObject(MyObjectInterface<? extends Number> o);
}
public class ConcreteCollection<T extends Number> implements MyCollectionInterface<T> {
List<MyObjectInterface<T>> list;
public void updateObject(MyObjectInterface<? extends Number> o) {}
}
private MyCollectionInterface<?> collection;
public ClientClass(MyCollectionInterface<?> collection){
this.collection = collection;
}
public void foo(MyObjectInterface<?> o){
this.collection.updateObject(o); //compiles
}
Also, note that even in 2) you could still run into the same problem in your implementation of the updateObject method unless you declare your list something like this (with ArrayList for example):
List<MyObjectInterface<? extends Number>> list = new ArrayList<MyObjectInterface<? extends Number>>();
In which case you could as well remove the <T extends Number> from MyCollectionInterface and ConcreteCollection since T isn't used anymore.
#Your last questions:
1) Probably yes
2) You should
3) You can't, if you don't really care which objects you store in the collection, you should ditch generics altogether.
Sorry for the long answer, hope this helps.

Incorrect generic type reported -- why?

Why doesn't this work?
public class FooImpl implements Foo { /* ... */ }
public class Main {
public static <T> Collection<T> getList(Class<? extends T> itemClass) { /* ... */ }
public static void main(String[] args) {
Collection<Foo> foos = getList(FooImpl.class);
}
}
On the line where foos is declared, I'm getting "Incompatible types. Required: Collection<Foo>, found: Collection<FooImpl>" error. Any idea why?
Try this :
Collection<Foo> foos = Main.<Foo>getList(FooImpl.class);
When you create your getList() method, it says that it will be Typed with T. And it also says that it will need a parameter of a subtype of T (a class of subtype of T to be precise).
As you never specify what will T be, getList suppose that it will be FooImpl so getList() returns a Collection of FooImpl.
With the solution I gave you, you specify that T is Foo, so the parameter will need to be a subtype of Foo. FooImpl for example.
Resources :
JLS - Generic methods
Because it is returning a collection of FooImpl while you like it to return a collection of its supertype. You need to define that a bit more explicitly:
public class FooImpl implements Foo { /* ... */ }
public class Main {
public static <S, T extends S> Collection<S> getList(Class<T> itemClass) { /* ... */ }
public static void main(String[] args) {
Collection<Foo> foos = getList(FooImpl.class);
}
}
Update: I had to mention that there's a JLS/javac bug related to this which caused it not to compile when using "plain vanilla" javac. See also javac bug 6369605, JLS bug 6369608 and this related question. It works fine in Eclipe however.
If you insist in using the javac over Eclipse, then your safest bet until the Java guys get it fixed is indeed to manually force the return type in the method call as demonstrated by Colin Hebert.
change getList return type to Collection<? extends T>
FooImpl is instance of a Foo
But
Collection<FooImpl> is NOT instance of Collection<Foo>
I agree with BaluC's answer.
Just for basic understanding of "extends" in generics check section The "extends" Wildcard Boundary
when using a wildcard with an upper
bound it is not safe to write to the
List. After all, a Car is always a
Vehicle, but a Vehicle is not always a
Car.

Java Generics Class Parameter Type Inference

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.

Categories

Resources