In the java.util.Collections class, we have two variants of the sort method, one that takes a list of arbitrary objects with a corresponding Comparator:
public static <T> void sort(List<T> list, Comparator<? super T> comparator)
And one that takes a list of Comparable objects:
public static <T extends Comparable<? super T>> void sort(List<T> list)
I was thinking how one woulds translate such method signatures with bounded wildcards into Scala. For the first version, I translated the signature literally and at first sight without compilation problems:
def sort[T](list: List[T], comparator: Comparator[_ >: T]) { ??? }
But then I found that I could not invoke this method with the following arguments:
val comparator = new Comparator[Object] {
def compare(o1: Object, o2: Object) = ???
}
val list = new ArrayList[Number]
sort[Object](list, comparator)
The last line gives this compile error, even though I explicitly specify the type T as Object.
type mismatch; found : java.util.ArrayList[Number] required: java.util.List[Object] Note: Number <: Object, but Java-defined trait List is invariant in type E. You may wish to investigate a wildcard type such as _ <: Object. (SLS 3.2.10)
Actually, I found out that it's even impossible to call the sole Java method directly as it fails with the same type of error.
Collections.sort[Object](list, comparator)
As for the version with comparable list, I came up with this declaration:
def sort[T <: Comparable[_ >: T]](list: List[T]) { ??? }
But this doesn't work at all:
illegal cyclic reference involving type T
What am I doing wrong? Are Scala variant generics following different rules the the Java ones? How call one call the Collections.sort method without actually getting a compile error?
Side note:
No, I'm not really asking how I can sort a list in Scala. I know that Scala has its own set of collections, sort functions and a different approach to comparing objects (such as Ordered and Ordering traits). My question concerns the general problem of generic methods and translation of generics from Java to Scala.
You are giving the wrong type parameter for T: You sort a List[Number], not a List[Object]:
sort[Number](list, comparator)
will work.
If you want to call sort without a type argument, you need to define two argument lists (because of how type-inference works in Scala):
def sort[T](list: List[T])(comparator: Comparator[_ >: T]) { ??? }
// Then
sort(list)(comparator)
You might want to consider using Scala types, that have proper support for covariance (i.e. in Scala a List[Number] is a List[Object]).
Concerning the version with comparable, you'll have to explicitly write the wildcard:
def sort[T <: Comparable[T], U <: T](list: List[U]) { ??? }
You could call the Java variant (or yours) with:
Collections.sort[Number](list, comparator)
The problem here is because Java generic types are invariant. In other words, this fails in Java:
List<Number> l1;
List<Integer> l2 = l1; //contravariance fails
List<Object> l3 = l1; //covariance fails
In Scala, generic type parameters can be declared to be covariant or contravariant in their declaration. Scala's List type parameter is declared to be covariant (which works because it's immutable). In other words, this is valid:
val l1: List[Number] = ???
val l2: List[Object] = l1 //valid
But since you're using Java's java.util.List that's not an option.
Related
I was reading about reasons why kotlin does not have wildcards (https://kotlinlang.org/docs/reference/generics.html). It all came to the declaration-site variance. We have <in T> and <out T> constructions which should replace wildcards. I think I understood how <out T> works but I have troubles with <in T>. So in java we could write something like this:
public List<? extends Number> list1;
public List<? super String> list2;
First case after initialization becomes read only list (though not perfectly immutable cause we could clear it) which could be read if we treat every element as Number.
Second case is write only (though we could read it if we treat every element as Object). We could write there String and it subclasses.
In Kotlin I was able to recreate list1 example using <out T> like this:
class Service {
val container = Container(mutableListOf("1", "2", "3"))
}
class Container<T>(var list1: MutableList<out T>)
Finally I tried something similar with <in T> thinking that I could recreate list2 example, but I failed:
Can someone explain to me how to achieve my list2 example in Kotlin? How should I use <in T> construction in proper way?
Kotlin List<E> is not equivalent to Java List<E>. The Java list has the mutating functions, while the Kotlin list is read-only. It's Kotlin MutableList<E> that is equivalent to the Java list.
Next, take a look at the List<E> declaration: its type parameter is covariant (out E), and the declaration-site variance cannot be overridden by use-site variance, that's why you cannot have a List<in T>.
Moreover, the declaration-site variance out E means that E never appears in an in-position (there's no function parameter of type E and no mutable property of type E), and indeed, since List<E> is read-only, it doesn't take E into any of its functions (*).
You can convert your example to use MutableList<E> instead:
class Container2<T>(var list2: MutableList<in T>)
The MutableList<E> interface has its E invariant, and the in-projection at use-site does not conflict with declaration-site variance.
(*) Actually, it does, but only using the parameters that are marked with the #UnsafeVariance annotation, which simply suppresses the variance conflict, more about it can be found here.
Also, a small remark:
It all came to the declaration-site variance. We have <in T> and <out T> constructions which should replace wildcards.
It's actually use-site variance that replaces Java wildcards. Declaration-site variance is only applied at the type parameter declaration (where the type is defined), and when it is, all the usages of the type will have that variance (e.g. when you use List<CharSequence>, it's actually a List<out CharSequence> because of the declaration-site variance of List<out E>). And use-site variance is, accordingly, for particular usages of a generic type.
I have a Problem with a generic method after upgrading to Java 1.8, which was fine with Java 1.6 and 1.7
Consider the following code:
public class ExtraSortList<E> extends ArrayList<E> {
ExtraSortList(E... elements) {
super(Arrays.asList(elements));
}
public List<E> sortedCopy(Comparator<? super E> c) {
List<E> sorted = new ArrayList<E>(this);
Collections.sort(sorted, c);
return sorted;
}
public static void main(String[] args) {
ExtraSortList<String> stringList = new ExtraSortList<>("foo", "bar");
Comparator<? super String> compGen = null;
String firstGen = stringList.sortedCopy(compGen).get(0); // works fine
Comparator compRaw = null;
String firstRaw = stringList.sortedCopy(compRaw).get(0); // compiler ERROR: Type mismatch: cannot convert from Object to String
}
}
I tried this with the Oracle javac (1.8.0_92) and with Eclipse JDT (4.6.1) compiler. It is the same result for both. (the error message is a bit different, but essentially the same)
Beside the fact, that it is possible to prevent the error by avoiding raw types, it puzzles me, because i don't understand the reason.
Why does the raw method parameter of the sortedCopy-Method have any effect on the generic type of the return value? The generic type is already defined at class level. The method does not define a seperate generic type. The reference list is typed to <String>, so should the returned List.
Why does Java 8 discard the generic type from the class on the return value?
EDIT: If the method signature of sortedCopy is changed (as pointed out by biziclop) to
public List<E> sortedCopy(Comparator c) {
then the compiler does consider the generic type E from the type ExtraSortList<E> and no error appears. But now the parameter c is a raw type and thus the compiler cannot validate the generic type of the provided Comparator.
EDIT: I did some review of the Java Language Specification and now i think about, whether i have a lack of understanding or this is a flaw in the compiler. Because:
Scope of a Declaration of the generic type E is the class ExtraSortList, this includes the method sortedCopy.
The method sortedCopy itself does not declare a generic type variable, it just refers to the type variable E from the class scope. see Generic Methods in the JLS
The JLS also states in the same section
Type arguments may not need to be provided explicitly when a generic method is invoked, as they can often be inferred (§18 (Type Inference)).
The reference stringList is defined with String, thus the compiler does not need to infer a type forE on the invocation of sortedCopy because it is already defined.
Because stringList already has a reified type for E, the parameter c should be Comparator<? super String> for the given invocation.
The return type should also use the already reified type E, thus it should be List<String>.
This is my current understanding of how i think the Java compiler should evaluate the invocation. If i am wrong, an explanation why my assumptions are wrong would be nice.
To bring an final answer to why this happens:
Like #Jesper mentioned already, you're using raw types when you shouldn't (Especially when using the Generic as type in multiple cases).
Since you pass an Comparator without an Generic-Type, there will actually be none. You could think of the E-Generic as null to make it easier. Therefore your code becomes to this:
public List sortedCopy(Comparator c) {
List sorted = new ArrayList(this);
Collections.sort(sorted, c);
return sorted;
}
Now you're attemptig/assuming you get an String from an List without Generics and therefore an Object (hence it's the super-class of everything ).
To the question why the raw-type parameter has no effect on the return type, since you don't specify an certain Level of abstraction. You'd have to define an Type that the Generic has to extend/implement at least to make that happen (compilation errors), for example.
public class ExtraSortList<E extends String> extends ArrayList<E> {
will now only allow Strings or Classes which extend it (not possible here since string is final). With that, your fallback Type would be String.
Consider the following 2 alternate APIs:
void method(List<?> list)
<T> void method(List<T> list)
I know that their internal implementation will have many differences to deal with, such as List<?> wont be able to write into the list etc.
Also, in my knowledge List<?> will allow any parameterized type with List as base type. So will be the case with List<T> also.
Can anybody tell me if there is any difference at all in what kinds of inputs these 2 APIs will accept. (NOT the 2 APIs internal implementation differences.)
The internal implementation is exactly the same. In fact, both methods compiled with javac will yield equal method byte code, if they compile at all).
However, during compilation, the first method is specified to not care about the component type of the list, while the second requires that the component type be invariant. This means that whenever I call such a method, the component type of list will be fixed by whatever the call site uses.
I can call my method with a List<String> and T will be synonymous to String during the call (from the perspective of the compiler). I can also call it with a List<Runnable> and T will be synonymous to Runnable during the call.
Note that your method does not return anything, but it very well could do so depending on the arguments. Consider the method:
<T> T findFirst(Collection<T> ts, Predicate<T> p) { … }
You can use this method for each T. BUT it only works if our T is equal for the collection and predicate — this is what "invariance" means. You could in fact specify the method to be applicable in more contexts:
<T> T findFirst(Collection<? extends T> ts, Predicate<? super T> p) { … }
This method would work the same as above, but it would be more lenient in what types it accepts. Consider a type hierarchy A extends B extends C. Then you could call:
Collection<A> cs = …;
Predicate<C> p = …;
B b = findFirst(cs, p);
We call the type of ts covariant and the type of p (in the method signature) contravariant.
Wildcards (?) are a different matter. They can be bounded (like in our cases above) to be co- or contravariant. If they are unbounded, the compiler actually needs a concrete type to fill in at compile time (which is why you will sometimes get errors like "type wildcard-#15 is not a match for wildcard-#17"). The specific rules are laid out in the Java Language Specification, Section 4.5.1.
why are these declaration invalid in Java?
List<Number> test = new ArrayList<? extends Number>();
List test = new ArrayList<? extends Number>();
are we not allowed to use wildcard during instantiation. and if the wildcards are only useful for passing them to methods?
and List<Object> test = new ArrayList<Integer>(); is illegal because generics are not covariant correct?
The ? wildcard character means "unknown" not "any". It doesn't make any sense to instantiate a new container of unknown contents, what would you put in there? It can't really be used for anything!
So the declaraion new ArrayList<? extends Number>() Means "some specific thing that extends number, but I don't know what." It does not mean "anything that extends number."
The List<Number> you assigned it to would allow both Double and Integer to be added to it, but the actual contents of a List<? extends Number> might be Float! (or whatever else.)
Consider what would happen in this code if the wildcard worked as an "Any":
List<Integer> listInteger = new ArrayList<Integer>();
listInteger.add(Integer.valueOf(1));
List<? extends Number> listWildCard = listInteger;
listWildCard.add(Double.valueOf(1.0)); //This does not compile
Integer integer = listInteger.get(1);//because this would throw a ClassCastException
Footnote regarding your second example:
Declaring a paramaterized type with no type parameter is called using the Raw Type. This is considered a programming error. The syntax is only legal so that code written before java 5 still compiles. Just don't do it if your scenario isn't backward compatability with pre-java 5.
To understand why it is not allowed to create objects of wildcard parameterized types, you must first understand what's the use of wildcard parameterized types.
Why wildcards?
As you already know that Java generics are invariant. So a List<Number> is not a super class of List<Integer>, even though their type arguments are covariant. So, what if you want such a behaviour in generics too, like having the same reference pointing to different objects? That polymorphic thing, as you would name it. What if you want a single List reference to refer to list of Integer, Float, or Double?
Wildcards to the rescue:
With wildcards, you can achieve the above mentioned behaviour. So, a List<? extends Number> can refer to a List<Integer>, List<Double>, etc. So, the following declarations are valid:
List<? extends Number> numbers = new ArrayList<Integer>();
numbers = new ArrayList<Double>();
numbers = new ArrayList<Float>();
numbers = new ArrayList<String>(); // This is still not valid (you know why)
So what did we change here? Just the reference type of the numbers. Note that generics were introduced in Java for enforcing stronger compile time check. So, it's primarily the compiler's job to decide whether the declaration of a parameterized type is conforming to the rule or not. Without wildcard, compiler shows you error for a List<Number> refering to List<Integer>.
So, wildcards are just a way to introduce co-variance like behaviour into generics. By using wildcards, you increase the flexibility, or you can say, reduce the restriction that compiler enforces. A List<? extends Number> reference tells the compiler that the list can refer to a list of Number or any subtype of Number(Of course, there are lower bounded wildcards too. But that's not the point here. The differences between them is already discussed in many other answers on SO only).
Major uses of wildcard parameterized type you would see with method parameters, where you want to pass different instantiation of a generic type for a single method parameter:
// Compiler sees that this method can take List of any subtype of Number
public void print(List<? extends Number> numbers) {
// print numbers
}
But at runtime, for creating an object you have to give a concrete type. A wildcard - bounded, or unbounded, is not a concrete type. ? extends Number could mean anything that is subtype of Number. So what type of List would you expect to be created when you create a List<? extends Number>?
You can consider this case similar to the reason why you can't instantiate an interface. Because they aren't just concrete.
There is a workaround. Really?
Although this is illegal, you would be surprised to know that there is a workaround, as explained in - Java Generics FAQs. But I really don't think you would ever need that.
When you instantiate a parameterized class, the parameter has to be some known, concrete type. It can be parameterized class even with ? but for inference reasons it has to be concrete. E.g. this is a valid declaration: new ArrayList<List<?>>();
The trick here is that the methods that use the type parameter in the arguments of their signature require the type of the argument to be lower bound. That is, any parameter that you pass in can be cast to the parameter type. Example:
public void fillUp(List<? super T> param)
The fillUp method takes a collection and fills it with T type objects. The param list must be able to handle the T objects so it is declared that the list can contain types that are ancestors of T, T can be safely cast to that type. If T was not a concrete type, like ? extends Number, then it would be impossible to exactly define all ancestors of T.
That's not a valid declaration as it's not a known type. You're not specifying a full type here. new ArrayList<Number> can accept anything that extends Number by subtyping so your use of ? extends Foo is not a valid need.
List<Number> can accept Integer, Long, etc. There's no way to do the equivalent of ? super Foo as it would be semantically meaningless beyond List or List<Object> with a strange artificial restriction.
Your current definition is not true, The generic type should be same in both sides or should be have inheritance relation.
Java generics are not covariant. See, for example, the article Java theory and practice: Generics gotchas by Brian Goetz. Your first example has two problems. First, when you instantiate a type it must be fully specified (including any type parameters). Second, the type parameters must exactly match the left side.
Regarding type covariance (or lack thereof), this is also not legal:
List<Number> test = new ArrayList<Integer>();
despite the fact that Integer extends Number. This also explains why the second example is illegal. A raw type is more or less the same as binding the type parameter to Object, so it would be like:
List<Object> test = new ArrayList<Integer>();
which again fails because generics are not covariant.
As to why the type parameters must be fully specified, the Java Language Specification, §8.1.2 explains the concept:
A generic class declaration defines a set of parameterized types (§4.5), one for each possible invocation of the type parameter section by type arguments.
You can only instantiate an actual type. As long as a type parameter of a generic type is unbound, the generic type itself is incomplete. You need to tell the compiler which specific parameterized type (among the set defined by the generic class) is being instantiated.
As to why generics are not covariant, this was intended to prevent the following sorts of errors:
List<Integer> iTest = new ArrayList<Integer>();
List<Number> test = iTest;
test.add(Double.valueOf(2.5));
Integer foo = iTest.get(0); // Oops!
Do not get confused by the Java inheritance concept and wildcard (e.g. ? here) syntex of Java generic concept. Both are not the same and none of the inheritance rule applies to java generic concept.
Hence Number is not same as ? extends Number
Please note that Java Generic wildcard is intended to tell the compiler about the intended object use. At runtime, it does not exist at all!
If you see generic in java just as a tool to prevent you to make mistakes, you should not go wrong in understanding this concept.
I have a class that maps incoming messages to matching readers based on the message's class. All message types implement the interface message. A reader registers at the mapper class, stating which message types it will be able to handle. This information needs to be stored in the message reader in some way and my approach was to set a private final array from the constructor.
Now, it seems I have some misunderstanding about generics and / or arrays, that I can't seem to figure out, see the code below. What is it?
public class HttpGetMessageReader implements IMessageReader {
// gives a warning because the type parameter is missing
// also, I actually want to be more restrictive than that
//
// private final Class[] _rgAccepted;
// works here, but see below
private final Class<? extends IMessage>[] _rgAccepted;
public HttpGetMessageReader()
{
// works here, but see above
// this._rgAccepted = new Class[1];
// gives the error "Can't create a generic array of Class<? extends IMessage>"
this._rgAccepted = new Class<? extends IMessage>[1];
this._rgAccepted[0] = HttpGetMessage.class;
}
}
ETA:
As cletus correctly pointed out, the most basic googling shows that Java does not permit generic arrays. I definitely understand this for the examples given (like E[] arr = new E[8], where E is a type parameter of the surrounding class). But why is new Class[n] allowed? And what then is the "proper" (or at least, common) way to do this?
Java does not permit generic arrays. More information in the Java Generics FAQ.
To answer your question, just use a List (probably ArrayList) instead of an array.
Some more explanation can be found in Java theory and practice: Generics gotchas:
Generics are not covariant
While you might find it helpful to
think of collections as being an
abstraction of arrays, they have some
special properties that collections do
not. Arrays in the Java language are
covariant -- which means that if
Integer extends Number (which it
does), then not only is an Integer
also a Number, but an Integer[] is
also a Number[], and you are free to
pass or assign an Integer[] where a
Number[] is called for. (More
formally, if Number is a supertype
of Integer, then Number[] is a
supertype of Integer[].) You might
think the same is true of generic
types as well -- that List<Number>
is a supertype of List<Integer>, and
that you can pass a List<Integer>
where a List<Number> is expected.
Unfortunately, it doesn't work that
way.
It turns out there's a good reason it
doesn't work that way: It would break
the type safety generics were supposed
to provide. Imagine you could assign a
List<Integer> to a List<Number>.
Then the following code would allow
you to put something that wasn't an
Integer into a List<Integer>:
List<Integer> li = new ArrayList<Integer>();
List<Number> ln = li; // illegal
ln.add(new Float(3.1415));
Because ln is a List<Number>, adding
a Float to it seems perfectly legal.
But if ln were aliased with li, then
it would break the type-safety promise
implicit in the definition of li --
that it is a list of integers, which
is why generic types cannot be
covariant.
It is right what cletus said. There is a general mismatch between the usually enforced invariance of generic type parameters vs. covariance of Java arrays.
(Some background: Variance specifies how types relate to each other concerning subtyping. I.e. because of generic type parameters being invariant
Collection <: Collection does not hold. So, concerning the Java type system, a String collection is no CharSequence collection. Arrays being covariant means that for any types T and U with T<:U, T[] <: U[]. So, you can save a variable of type T[] into a variable of type U[]. Since there is a natural need for other forms of variance, Java at least allows wildcards for these purposes.)
The solution (the hack, actually) I often use, is declaring a helper method which generates the array:
public static <T> T[] array(T...els){
return els;
}
void test(){
// warning here: Type safety : A generic array of Class is created for a varargs parameter
Class<? extends CharSequence>[] classes = array(String.class,CharSequence.class);
}
Because of erasure the generated arrays will always be of type Object[].
And what then is the "proper" (or at least, common) way to do this?
#SuppressWarnings(value="unchecked")
public <T> T[] of(Class<T> componentType, int size) {
return (T[]) Array.newInstance(componentType, size);
}
public demo() {
Integer[] a = of(Integer.class, 10);
System.out.println(Arrays.toString(a));
}
Arrays are always of a specific type, unlike how Collections used to be before Generics.
Instead of
Class<? extends IMessage>[] _rgAccepted;
You should simply write
IMessage[] _rgAccepted;
Generics don't enter into it.
IMHO,
this._rgAccepted = (Class<? extends IMessage>[])new Class[1];
is the appropriate way to handle this. Array component types have to be reified types, and Class is the closest reified type to Class<whatever>. It'll work just as you would expect a Class<whatever>[] to work.
Yes, technically it is unchecked and might cause issues later if you cast it to another more general array type and put wrong stuff in it, but since this is a private field in your class, I presume you can make sure it is used appropriately.