Generic method does not compile when passed a generic argument - java

I found a strange inconsistence when implementing a Java interface that makes heavy use of generics and I can't find an explanation to why this happens.
I've stripped down the example as much as possible and I'm aware that the usage of generics makes not much sense in this example anymore.
public interface Service<T> {
public List<T> thisCompiles();
public List<T> andThisCompiles(List<Object> inputParam);
public <S extends T> List<S> thisCompilesAswell();
public <S extends T> List<S> evenThisCompiles(List inputParam);
public <S extends T> List<S> butThisDoesnt(List<Object> inputParam);
}
public class ServiceImpl implements Service<Number> {
#Override
public List<Number> thisCompiles() {
return null;
}
#Override
public List<Number> andThisCompiles(List<Object> inputParam) {
return null;
}
#Override
public List<Number> thisCompilesAswell() {
return null;
}
#Override
public List<Number> evenThisCompiles(List inputParam) {
return null;
}
#Override
public List<Number> butThisDoesnt(List<Object> inputParam) {
return null;
}
}
Please note that in the implementation, all return types are List<Number>, although the interface is more generous.
When compiling this snippet (I tried Oracle JDK 8 u144 and OpenJDK 8 u121), I get the following error messages:
The method butThisDoesnt(List<Object>) of type ServiceImpl must override or implement a supertype method
The type ServiceImpl must implement the inherited abstract method Service<Number>.butThisDoesnt(List<Object>)
Name clash: The method butThisDoesnt(List<Object>) of type ServiceImpl has the same erasure as butThisDoesnt(List<Object>) of type Service<T> but does not override it
It seems that compilation fails as soon as the parameter that I pass to the function has some generic parameter itself.
Implementing the 5th function as
#Override
public <S extends Number> List<S> butThisDoesnt(List<Object> inputParam) {
return null;
}
works as expected.
Is there an explanation for this sort of behaviour or did I stumble upon a bug (although it happens in OpenJDK aswell)? Why is the 5th function behaving differently?
I am not interested in finding out how I can make this code compile (I fixed it for my codebase). I just stumbled upon this problem and am curious about the logical explanation why exactly the compilers accept the first four methods but reject the fifth.

I have included a simplified version of the issue. In the following example, the program will not compile because of the method broken.
import java.util.List;
import java.util.ArrayList;
public class Main{
static interface Testing{
<S> List<S> getAList();
<S> List<S> broken(List<String> check);
}
static class Junk implements Testing{
public List<Number> getAList(){
return new ArrayList<>();
}
public List<Number> broken(List<String>check){
return new ArrayList<>();
}
}
public static void main (String[] args) throws java.lang.Exception
{
// your code goes here
}
}
There are two errors and one warning.
Main.java:11: error: Junk is not abstract and does not override
abstract method broken(List) in Testing static class Junk
implements Testing{
^ where S is a type-variable:
S extends Object declared in method broken(List)
Main.java:12: warning: [unchecked] getAList() in Junk implements
getAList() in Testing public List getAList(){
^ return type requires unchecked conversion from List to List where S is a type-variable:
S extends Object declared in method getAList()
Main.java:15: error: name clash: broken(List) in Junk and
broken(List) in Testing have the same erasure, yet neither
overrides the other public List broken(Listcheck){
^ where S is a type-variable:
S extends Object declared in method broken(List)
2 errors
1 warning
Why does the first method infer a cast and only give a warning?
"An unchecked conversion is allowed in the definition, despite being unsound, as a special allowance to allow smooth migration from non-generic to generic code."
https://docs.oracle.com/javase/specs/jls/se8/html/jls-8.html#jls-8.4.5
The other method has a Generic in the argument list, therefor it is not a migration from previously non-generic code, and it is unsound and should be considered an error.

The Problem with your fifth method is that one might think that following snippet works:
List<Integer> ints = new ArrayList<>(); // this works as expected
List<Number> number = new ArrayList<>(); // this works too
number = ints; // this doesn't work
That is, because with generics there exists no such thing as hierarchy relation and so List<Number> is in no sense related to List<Integer>.
This is due to type erasure, generics are only used during compilation. At runtime you only have List-objects (type erasure = no type available anymore at runtime).
In the interface the method is defined as <S extends T, V> List<S> butThisDoesnt(List<V> inputParam); where S can be any subtype of Number. If we now apply the above mentioned to your implementation, then it should make sense.
public List<Number> butThisDoesnt(List<Object> inputParam) {
return null;
}
List<Number> clashes with the definition in the interface, in that way, because List<Number> can contain any Number but can not be a List<Integer>.
Read more to type erasure in this other question. And on this page.
Note please correct me if i'm wrong or if I did miss something.

Related

Unchecked call to 'forEach()' as a member of raw type 'java.lang.Iterable'

I am getting this compiler warning.
This is my class with interface and method used (other staff omitted):
public class Controller extends BaseController {
//interface
public interface MyInterface<T extends Iterable<? super T>> {
List<T> getList();
}
//method call
updateJoinTable(request.getProductGrades().size(), instance::getProductGrades);
//method
private static void updateJoinTable(Integer LastValidElement, MyInterface myInterface) {
myInterface.getList().subList(LastValidElement, myInterface.getList().size())
.forEach(i ->myInterface.getList().remove(i));
}
}
The last part with forEach is causing warning.
Now, at first the code was without:
<T extends Iterable<? super T>>
but I've seen a lot of similar cases here on SO, mostly with comparable and I understood by reading the solutions, that problem is that my generic types are bonded to a type that itself has a type, but I haven't provided one, so it's raw - then I added that line to interface and expected warning to go away. But it's still there.
Do you have any ideas what I should do to get rid of it?
The immediate issue is that MyInterface myInterface is a raw type. Make it non-raw, e.g.:
private static void updateJoinTable(Integer LastValidElement, MyInterface<?> myInterface) {
Additionally, you might want to consider not using forEach. It looks like you're just trying to chop off the tail of the list:
List<?> list = myInterface.getList();
list.subList(LastValidelement, list.size()).clear();

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

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);

What is the type erasure of "?"?

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

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.

Generic Method Covariance - valid restriction or compiler oversight?

Does anyone know the definitive answer to why the following isn't allowed by the java compiler?
class BaseClass {
public <T extends Number> T getNumber(){
return null;
}
}
class SubClass extends BaseClass{
#Override
public <T extends Integer> T getNumber(){
return null;
}
}
this causes the compiler to complain with:
"The method getNumber() of type SubClass must override a superclass method"
Now, when I put this to my colleagues some have tried to explain that it will cause confusion with the compiler. However, as was also pointed out the following, which is conceptually similar, is compilable.
class BaseClass<T extends Number> {
public T getNumber(){
return null;
}
}
class SubClass<T extends Integer> extends BaseClass<T>{
#Override
public T getNumber(){
return null;
}
}
This can be abused if the subclass calls the super implementation, but the compiler provides a warning to this effect. My only conclusion is that this is a compiler oversight on the part of folks at Sun (can't bring myself to say Oracle :-S).
Anyone have the definitive answer to this one?
Suppose it would indeed be allowed to add more restrictions to the type parameter on a derived class.
Then what if your class also had a <T extends Number> void setNumber(T number) method?
BaseClass foo = new SubClass();
long longValue = 42;
foo.<Long>setNumber(longValue);
The above would be accepted by the compiler because BaseClass.setNumber accepts any type parameter derived from Number. But the actual instance only accepts integers!
You could argue that if the type parameter is only used for the return value, it should automatically be considered covariant. But then the compiler would have to make sure that you don't use the type parameter inside the method body in a non-covariant way.
(In C# 4.0 this was actually solved, but it involves explicitly marking your type parameters as covariant or contravariant with the out and in keywords. It was not something that could simply be allowed by the compiler without changing the language.)
Because there is no covariance here. Covariance means 'varies with', and it refers to the return type varying as the containing class type. That's not happening here. There is no 'co' in your covariance.
#Wim Coenen provided a good answer.
Here is a small clarification.
If this would compile, what would you expect happened:
import java.math.BigInteger;
class BaseClass {
public <T extends Number> T getNumber(T n) {
System.out.println("Int value: " + n.intValue());
return n;
}
}
class SubClass extends BaseClass {
#Override
public <T extends BigInteger> T getNumber(T n) {
System.out.println("Int value: " + n.intValue());
// BigInteger specific!
System.out.println("Bit count: " + n.bitCount());
return n;
}
}
public class Main {
public static void main(String[] args) {
BaseClass sub = new SubClass();
// sub looks like a BaseClass...
// ...but since it's a SubClass it expects a BigInteger!
sub.getNumber(new Integer(5));
}
}
I don't quite follow your issue:
When I implemented your first portion of source code and compiled using javac *.java, it compiled fine. However, if I changed your SubClass.getNumber() method to do this...
/* (non-Javadoc)
* #see constraint.base.BaseClass#getNumber()
*/
#Override
public <T extends Number> T getNumber() {
// TODO Auto-generated method stub
return super.getNumber();
}
I get the following error:
SubClass.java:18: type parameters of <T>T cannot be determined; no unique maximal instance exists for type variable T with upper bounds T,java.lang.Number
return super.getNumber();
^
1 error
The reason for the code above is that by calling super.getNumber(), the compiler doesn't have any input that infer type T hence it cannot determine what T will be returned by super.getNumber() and, thus it can't satisfy the return type with SubClass.getNumber()
Now, in order to have obtained the error from the compiler, "The method getNumber() of type SubClass must override a superclass method", your BaseClass should have been abstract along with it's getNumber() method.
As for the 2nd piece of code you've provided, the subclass can, in effect, call super.getNumber() as the compiler will know (on compiling) that the SubClass's Generic Parameter Type is inferred to the Parameter Type of BaseClass.
Besides that, I really didn't know what you were trying to ask.

Categories

Resources