Kotlin Generics declaration-site variance <in T> construction - java

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.

Related

Using wildcards in stream terminal operations

Hi2all.
I'am reading a Java book and got to the chapter about StreamAPI classes.
So, my questions is: why in terminal operation methods using lower bounded wildcards, like:
void forEach(Consumer<? super T> consumer)
boolean anyMatch(Predicate <? super T> pred)
Optional<T> min(<? super T> comparator)
Indeed, in fact, you can use unbounded wildcards like this:
void forEach(Consumer<?> consumer)
boolean anyMatch(Predicate <?> pred)
Optional<T> min(<?> comparator)
Is there any reason to use exactly lower- bounded wildcards?
You cannot use unbounded wildcards for these methods.
Suppose you are implementing your own forEach method. If it accepts a Consumer<? super T>, you can pass a T to it, because you know that T is a subtype of whatever the Consumer's parameter's type is and can therefore safely make the consumer accept an element of type T.
However, if it accepts a Consumer<?>, you can't directly pass a T to it without casting. There is no guarantee that the consumer accepts Ts and not some other type? Furthermore, as Louis Wasserman pointed out in his answer, you would be able to pass a Consumer<Long> to the forEach method of a Stream<String>.
Lower-bounded wildcards are as loose as you can get while also being typesafe. With just plain Consumer<T> and Supplier<T> parameters, you wouldn't be able to directly pass a Consumer<Object> to a Stream<String>'s forEach method without casting. With unbounded wildcards, you would be able to pass basically anything, which defeats the whole point of using generics.
You're wrong in saying that you can use "unbounded wildcards." You can't, and the terminal operations you describe have the most permissive types possible.
Consumer<Long> longConsumer = (l) -> System.out.println(l * 2);
Stream<String> stringStream = Stream.of("a", "b");
stringStream.forEach(longConsumer);
// should not work, but would work if forEach accepted Consumer<?>

Java Generics - List<?> vs List<T>

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.

generics unchecked warning conversion

I have read in generics that "? extends Object" and "?" are synonymous then why this occurs.
List list=new ArrayList();
List<? extends Object> list2=list; //1
List<?> list3=list; //2
For 1 unchecked conversion warning is thrown but not for 2. So the compiler somewhere is definitely differentiating between the two.
Plz explain the difference between the two with respect to the above code
I have read in generics that "? extends Object" and "?" are synonymous
Not quite. The first wildcard has a lower bound, the second does not. For your two examples it should not make a difference (well, except that you can only add null to list2 and list3!).
This lower bound can make a difference: "erasure signature" (I don't know the exact term).
The best example for this is Collections.max(); you will notice that the parameter type is defined as T extends Object & Comparable<? super T>.
This is because prior to Java 5 this method existed and was defined as:
static Object max(Collection coll)
If the type parameter were defined as T extends Comparable<? super T>, this would have meant that the method in 1.4 would have had to return a Comparable!
Because some type information is erased during compilation, not all types are available at run time. Types that are completely available at run time are known as reifiable types(see http://docs.oracle.com/javase/specs/jls/se7/html/jls-4.html#jls-4.7).
So according to JLS List<?> is a reifiable type, but List<? extends Object> is not, which means they are not the same from compiler point of view.

Universal qualifier in Java generics?

I understand the <? extends T> in Java is crudely equivalent to the existential qualifier (∃) but is <? super T> related to the universal qualifier (∀)?
Feel free to correct me if I'm wrong about ∃.
It's early and I'm confused, so may well be talking nonsense. I could buy that both are existential, as direction need not negate the logic for a "for some" relationship..?
Partially prompted by the fact that List[_] in Scala is described as existential and that's roughly the same as List<?> in Java.
Wildcard types (both ? extends T and ? super T) form a subset of existential types. Both of these you can read like "there exists some type which (extends|is supertype of) T". The key idea is that you don't know exact type.
Universal types are just type parameters. For example, here:
class List<T> { ... }
T is arbitrary, like it has an implicit universal qualifier.
Its not really related to predicate logic, its related to the type hierachy. I can understand why you might read out loud <? extends T> as "For all T's" but its extended to "For all objects that have a super class of T" rather than "For all T in X" as in predicate logic.
Similarly <? super X> should be read as "For objects that are of a type that is a super class on X"
The main difference between <? super T> and <? extends T> is that the first declaration says that some type is an ancestor of T and the second says that some type is a subclass of T. I don't see any correlation with existential qualifier or universal qualifier here. As someone noticed in the comments, the more likely similarity (if you really must have one) is type floor and ceiling. Nevertheless, it's really an abstract analogy.

Translating generic wildcards from Java to Scala

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.

Categories

Resources