I am reading multi-level wild cards from AngelikaLangerGenericsFaq. I am pretty confused
about the syntax. The document says
The type Collection<Pair<String,?>> is a concrete instantiation of the
generic Collection interface. It is a heterogenous collection of
pairs of different types. It can contain elements of type
Pair<String,Long> , Pair<String,Date> , Pair<String,Object> ,
Pair<String,String> , and so on and so forth. In other words,
Collection<Pair<String,?>> contains a mix of pairs of different types
of the form Pair<String,?> .
The type Collection<? extends Pair<String,?>> is a wildcard
parameterized type; it does NOT stand for a concrete parameterized
type. It stands for a representative from the family of collections
that are instantiations of the Collection interface, where the type
argument is of the form Pair<String,?> . Compatible instantiations
are Collection<Pair<String,Long>> , Collection<Pair<String,String>> ,
Collection<Pair<String,Object>> , or Collection<Pair<String,?>> . In
other words, we do not know which instantiation of Collection it
stands for.
As a rule of thumb, you have to read multi-level wildcards top-down.
I am confused about the following points.
Can someone elaborate on these three quotes with example. I am totally lost into the syntax
Document says, para-1 is the concrete instantiation of a generic type and other is not the concrete instantiation? How is that?
What does it mean to read the wild-cards top down?
What is the advantage of multi-level wild cards?
Can someone elaborate these points. Thanks.
Can someone elaborate on these three quotes with example. I am totally lost into the syntax
Well, it wouldn't make sense to write those 3 quotes again here, as I can't give a better explanation than that. Instead, I will try to answer your other questions below, then possibly you will understand the answer to this one too. If not, you can ask your query again and I'll try to elaborate a little further.
Document says, para-1 is the concrete instantiation of a generic type and other is not the concrete instantiation? How is that?
A concrete instantiation is the one in which all the type arguments are concrete types, and are known at compile time. For e.g., List<String> is a concrete instantiation, because String is a concrete type. Its type is known at compile time. Whereas, List<? extends Number> is not a concrete type, because ? extends Number can be any type that extends Number. So, its type is unknown at compile time. Similarly, Map<String, Integer> is a concrete instantiation of generic type Map<K, V>.
In the case of multi-level type parameters, List<List<? extends Number>>, the outer List is a concrete instantiation of List<E>, because the type of elements is known to be a List at compile time, although the inner List is a wildcard instantiation, as the type of elements stored can be Integer, Double, any subclass of Number. But that paragraph is talking about the outer type only. And the outer type can only contain List type.
That's why the first paragraph said, it's a heterogenous collection of Pair, because the actual type parameter of Pair can be anything, but that is certain to be Pair and nothing else.
What does it mean to read the wild-cards top down?
Talking in layman's term, it means from left-to-right. While determining the type of the parameterized type, you first see the outermost type parameter. Then if that type parameter is itself a parameterized type, then you move onto the type parameters of that parameterized type. So, we read the type parameters, from left-to-right.
What is the advantage of multi-level wild cards?
Suppose you want to create a List of List of Fruits. Now your inner List can contain any kind of of fruits. An apple is also a fruit, and a banana is also a fruit. So, you have to make sure that you get all of them. Now, since generic types are invariant, in the sense, List<Apple> is not the same as List<Fruit>, you can't add a List<Apple> if your type of list is List<List<Fruit>>. For that you would need to use wildcards like this - List<List<? extends Fruit>>, which can now take List<Apple>, List<Banana>, list of any fruit.
Generic types with wildcards are really "existential" types. If you're familiar at all with logic, you can read G< ? extends T > as ∃S extends T:G< S >.
Angela's explanation about reading types "top down" really means that the imaginary existential quantifier implied by a type that contains a ? in it is always as close as possible to the ?. For example, you should mentally rewrite G< H< ? extends T > > to G<∃S extends T:H< S > >. Since there's no quantifier on the outside, it's called concrete.
Related
In a modern functional language like Scala, type variance is inherent in the type. Here's e.g. Scala's Function1:
trait Function1[-T1, +R] { ... }
contravariant in parameter type and covariant in return type. And here's java's counterpart:
interface Function<T,R> { ... }
Now, to express variance relationship, the "wildcard capture" special syntax is used. For example, the stream's map function is declared as
<R> Stream<T> map(Function<? super T, ? extends R> mapper);
Here, Java shifts the declaration of variance relationship from the type itself to its use as a param in some method signature.
Here's my question. Would I be amiss to say that there cannot be any legitimate usages of Function<T,R> that are not contravariant in T and covariant in R? In other words, does Java's way offer useful extra flexibility not found in Scala, or is it just a lot of repetitive unwieldy boilerplate?
For Function specifically, no. Function defines exactly one abstract method, apply, which uses T contravariantly and R covariantly. But Function isn't what they had in mind when they designed that feature.
When the Java devs designed call-site variance, they were imagining classes that had both covariant and contravariant uses. For instance, in principle, the E in List<E> must be invariant. It appears in covariant position in get and in contravariant position in add.
So the rationale was this. Suppose we have a type hierarchy X <= Y <= Z. That is, X is a class that subclasses Y, and Y in turn subclasses Z. A List<Y> can do anything with type Y. It can have Ys added to the end, and a user can retrieve elements of type Y from it. But it can never be a List<Z> or a List<X>, since adding to a List<X> would be unsound, and so would retrieving as a List<Z>.
But we can express our intention. List<? extends Y> is a type we can only ever read from. It actually can take a List<Z> under the hood, since a list of Z elements is genuinely still (at least for covariant methods) a list of Y elements. We can get elements from this list, but we can't add to the end of it, since we said we're using the type argument in covariant position but add uses the type argument contravariantly. Essentially, List<? extends Y> is a smaller interface that includes some of the methods from the actual interface List.
The same is true of List<? super Y>. We can't read from it, since we don't know that every element is of type Y. But we can add to it, since we know that the list at least supports elements of type Y. We can use all of the contravariant methods, like add, but none of the covariant ones.
For a type like List that uses its type arguments in different ways, the call-site variance makes some amount of sense. For a special-purpose interface like Function that does one thing, it makes little sense.
That was the Java developers' rationale some twenty years ago when generics were added to Java. A lot has happened since then. If someone wrote an interface like List in today's world, an interface with upwards of 20 abstract methods, half of which have "this method may not be supported and might just throw UnsupportedOperationException" built-in to the contract, they'd rightly be laughed off the stage.
Today's world is one of small, tight interfaces. We follow the SOLID principles. An interface does one thing and does it well. If an interface defines more than two or three (non-defaulted, non-inherited) methods, we give pause and ask if we can make it more modular. And we try to design systems that are more immutable by design, to support scaling and concurrency. We have records, or data classes or whatever your favorite language calls them, that are immutable by default.
So twenty years ago, the idea of a massive super-interface that does twenty things and that can be narrowed down dynamically via type projections seemed pretty cool. Today, it makes far more sense to specify the variance at the declaration site, since most interfaces are small and have a clear use case in mind.
The scala.collection.Seq trait defines three abstract, non-inherited methods (apply, iterator, and length), and all of those use the type argument covariantly, so Seq is defined with a covariant type. The corresponding mutable trait adds one more method (update), which uses its type argument contravariantly, so it has an invariant argument.
In Scala, if you want to modify a sequence, you take a scala.collection.mutable.Seq. If you want to read, you take a scala.collection.Seq. And those interfaces are small enough and narrow enough in purpose that the fact that there are several of those doesn't affect the code quality (and the fact that traits and classes in Scala are cheap to write, compared to the boilerplate necessary in Java to make even a simple class).
Actually, Scala supports both declaration and use site variance. Specifically, you can specify bounded wildcards just like in Java.
This already hints that declaration site variance can not replace use site variance in all cases. The reason is that a declaration can only be variant if it is variant in all possible uses. If some uses are variant, but other uses are not, we can't use declaration site variance, but we can use use site variance.
For instance, class Array[A] can not be declared variant, but the method appendedAll from ArrayOps can employ use site variance
def appendedAll[B >: A](suffix: Array[_ <: B])(implicit arg0: ClassTag[B]): Array[B]
since it uses covariant methods of suffix.
In other words, does Java's way offer useful extra flexibility not found in Scala, or is it just a lot of repetitive unwieldy boilerplate?
Suppose that class Foo extends class Parent and is in turn extended by class Child.
Then, as you know, we can pass an instance of ArrayList<Foo> to a method that takes a List<? extends Parent>, or to a method that takes a List<? super Child>.
A reason that a method might take List<? extends Parent> is if it only reads from the list (it never writes to it), and can happily support any element that's an instance of Parent (because it doesn't need anything specific to Foo or another subtype).
A reason that a method might take List<? super Child> is if it only writes to the list (it never reads from it), and the elements that it writes are always instances of Child (so it doesn't care whether the list can accept arbitrary instance of Foo or another supertype).
That said, yes, it is a lot of repetitive unwieldy boilerplate! As a result, it's not uncommon to come across a method that could take a List<? extends Parent> or a List<? super Child> but instead just takes a List<Parent> or a List<Child> (respectively).
I am not an expert on this but it can be argued that the use of wildcard capture syntax in Java's Function interface allows for more flexibility in expressing variance relationships compared to Scala's Function1 trait, where variance is inherent in the type. The wildcard capture syntax allows for variance to be declared specifically in the context of a method's parameter or return type, rather than being inherent to the type itself. However, it can also be seen as repetitive and unwieldy boilerplate. It depends on the perspective of the developer and their use case.
I have just came accross a question which I don't quite understand.
I have following variable (class field):
List<Validator<?, Data>> validators;
Now I want to assign some validators' instances to this list, then:
validators = Arrays.asList(validatorsFactory.create(Obj1.class));
Where the create method returns Validator<?, Data>
And there is a problem: I need to specify generic type of that list, due to wildcards. Okay, right version is:
validators = Arrays.<Validator<?, Data>>asList(validatorsFactory.create(Obj1.class));
But, when I add two elements instead of one I dont have to specify any generic type. How does Java know that the wildcards are the same?
validators = Arrays.asList(
validatorsFactory.create(Obj1.class), validatorsFactory.create(Obj2.class)
);
My understanding of that was that all wildcards are different placeholders when they are an arguments to a method.
Same applies to Guava's: ImmutableList.of
Thanks in advance
When you have an argument with a wildcard at the top level, e.g. Validator<?, Data>, you are allowed to pass it to a generic method with a type variable in the place where the wildcard is, e.g. public <T> void foo(Validator<T, Data>). This is called wildcard capture.
This case is a little different, because the Arrays.asList() takes parameters of type T. For some reason in your first case, it is keeping the capture when inferring the type of T, so it infers T to be Validator<capture #XXX, Data>, which is not the T that you want.
In your second case, I am guessing that the fact you have two arguments, both of type Validator<?, Data>, somehow makes it impossible for T to be inferred to contain a capture, since the two wildcards would have two different captures, so it just infers T to be Validator<?, Data>, which is what you wanted in the first place.
I've just been diving into Java generics and have come across something puzzling. I have a List of Class objects. These Class objects are of classes that extend the class MyType. What I want to do is the following:
Class<? extends MyType>[] myArray = myList.toArray( Class<? extends MyType>[] );
Now I know this won't work. But how is it possible to create such an array? My trouble is that I need to pass this List as a Class array to a method. I'd rather avoid #SuppressWarnings and figure out if it's possible.
You can pass the list as such but not instantiate as such. Must be instanced with concrete class substitutiona but then be assigned to whatever the captures.
There is rule "don't mess arrays with generics". There is the restriction in Java that states that creation of arrays of parameterized types is not allowed. So, the answer for your question is no, you can't create an array of any generic types.
UPD: Consider using any collection type instead of arrays. They will work just fine.
Or you can (but better not do that though) use array of non-generic elements and then cast them to appropriate type, suppressing warnings.
You have to provide the concrete type here which you also should know because of the type of myList.
I'm having difficulty understanding wildcards in Java generics. Specifically I have the following questions:
If we have a LinkedList<?>, why can we not add an Object to it? I understand that it doesn't know the type of the list, but wouldn't adding an Object to the list have us covered in any situation?
Similar to the question above, if we have LinkedList<? extends Number>, why can we not add a Number to it?
Finally, if we have LinkedList<? super Number>, why can we add an Integer to the list, shouldn't we only be able to add things that are a superclass of Number?
I guess I'm trying to understand how wildcards work in general, I've read the Oracle tutorials on them, and a few other things, but I don't understand why they work I suppose.
You're confusing objects and types.
Unlike simple generic parameters, wildcards describe the type of the generic parameter.
A List<? super Number> isn't a list of superclasses of Number; it's a list of some unknown type, where that type is a superclass of number.
A LinkedList<?> might be a LinkedList<Car>.
Since an Object is not a Car, you can't add an Object to it.
In fact, since you don't know anything about what type the list contains, you can't add anything to it. (except null)
Similarly, a LinkedList<? extends Number> might be a List<Long>, so you can't add an Integer to it. (since an Integer is not a Long)
On the other hand, a List<? super Number> is definitely allowed to contain Number or any derived class, since it can only be a list of one of Number's superclasses (eg, List<Object>)
A book I am reading on Java tells me that the following two pieces of code are equivalent:
public <T extends Animal> void takeThing(ArrayList<T> list)
public void takeThing(ArrayList<? extends Animal> list);
On the opposite page, I am informed that the latter piece of code uses the '?' as a wildcard, meaning that nothing can be added to the list.
Does this mean that if I ever have a list (or other collection types?) that I can't make them simultaneously accept polymorphic arguments AND be re-sizable? Or have I simply misunderstood something?
All help/comments appreciated, even if they go slightly off topic. Thanks.
Does this mean that if I ever have a list (or other collection types?) that I can't make them simultaneously accept polymorphic arguments AND be re-sizable?
No.
The two pieces of code are not completely equivalent. In the first line, the method takeThing has a type parameter T. In the second line, you're using a wildcard.
When you would use the first version, you would specify what concrete type would be used for T. Because the concrete type is then known, there's no problem to add to the list.
In the second version, you're just saying "list is an ArrayList that contains objects of some unknown type that extends Animal". What exactly that type is, isn't known. You can't add objects to such a list because the compiler doesn't have enough information (it doesn't know what the actual type is) to check if what you're adding to the list should be allowed.
Usually, if adding to a list is involved inside a method that accepts just the list without the thing to add, you'll have somewhere else something that is an Animal and you'll want to add it to the list. In this case your method must be declared so that all the list types it accepts allow adding an Animal into them. This will have to be a List<Animal> or a list of some supertype of Animal. It can't possibly be a List<Dog>— the element you are adding could be any Animal.
This is where the concept of the lower bound, and the keyword super, come in. The type declaration List<? super Animal> matches all the acceptable types as described above. On the other hand, you won't be able to get elements out of such a list in a typesafe way because they can in general be of any type at all. If a method wants to both add and get elements of declared type Animal, the only valid type it can accept is a List<Animal>.