Java generics: creating collections of class objects extending Throwable - java

Why does the first line work, but the second one doesn't?
Collection<Class<? extends Throwable>> exs =
new ArrayList<Class<? extends Throwable>>() {{ add(MyOwnException.class); }};
Collection<Class<? extends Throwable>> exs = Arrays.asList(MyOwnException.class);

The reason it's an error is that java is inferring the wrong type, but you can make it compile, without casting, by specifying the type in the call to the typed method Arrays.asList():
Collection<Class<? extends Throwable>> exs
= Arrays.<Class<? extends Throwable>>asList(Exception.class); // compiles
Without specifying the type, java infers the element type of the collection to be Class<Exception>, which is not assignable to Collection<Class<? extends Throwable>>.
Remember with generics that if B extends A, List<B> does not extend List<A>.

Generics are very tricky.
In this case Arrays.asList will return a List<Class<MyOwnException>>, which is not the same than List<Class<? extends Throwable>>.
Their behavior will differ since the type List<Class<? extends Throwable>> would allow you to add objects that extend Throwable but List<Class<MyOwnException>> only accept objects of type MyOwnException.

The first line works because the type of the element of the right side of the assignement (of the ArrayList) is Class<? extends Throwable> that allows to add any kind of Class<? extends Throwable> object, including your MyOwnException. You could also add to the first line Collection an Exception.class or a NullPointerException.class, etc.
But the second line is much stricter: you have a Collection<Class<? extends Throwable>> that might contain a Class<Exception>, a Class<NullPointerException>, etc. and you want to put in place a Collection<Class<MyOwnException>> that don't allows for Class<Exception>, etc.

Arrays.asList(MyOwnException.class) is inferred to have type List<Class<MyOwnException>>, which is not compatible with List<Class<? extends Throwable>> since the type parameters are different.
If you put a wildcard at the first-level, it should work:
Collection<? extends Class<? extends Throwable>> exs = Arrays.asList(MyOwnException.class);

If the second example were to work, it would allow for code like this:
Collection<Class<? extends Throwable>> exs = Arrays.asList(MyOwnException.class);
exs.add(IOException.class);
See the problem? Arrays.asList() would return a List<Class<MyOwnException>>, and then you'd be trying to insert a Class<IOException> into that. That's obviously a type mismatch.

Related

Insert a BaseClass instance into a List<? extends BaseClass> (Java)

I have a List<? extends MyClass> list and I want to add into this list objects whose class extends MyClass and objects which are instances of MyClass:
list.add(new ClassThatExtendsMyClass());
list.add(new MyClass());
but eclipse shows the build error:
The method add(capture#9-of ? extends MyClass) in the
type List is not
applicable for the arguments (MyClass)
How can I get around this?
Java keyword extends in generic type of reference is used to restrict actual generic type realization to be no less derived than the named type. Especially it can be a subclass. One should use it in case type is used to extract values and operate on them. You then use the interface of the base class not concerning the actual type (realization/implementation) of that class.
When you've got a specific class and need to pass (put) into collection, or other generic class object, the reference to point at that object should have super keyword, which restricts the actual object to be no more derived than the named type.
Code example below. Assume we have classes Base and Derived, where Derived extends Base, class Base have an interface method foo() and both have default constructor.
void service() {
List<Base> theList = new ArrayList<Base>();
produceObjects(theList);
consumeObjects(theList);
}
void produceObjects(List<? super Base> consumerList) {
consumerList.add(new Derived());
}
void consumeObjects(List<? extends Base> producerList) {
for (Base base : producerList) {
base.foo();
}
}
You can declare your reference as List<MyClass>.
You'll be able to add anything extending MyClass, including instances of MyClass.
This is not possible. List<? extends MyClass> list could actually point to a ArrayList<ClassThatExtendsMyClass>. Which can't hold instances of some other subclass of MyClass.
List<? extends Number> numbers = new ArrayList<Integer>();
numbers.add(Long.valueOf(2)); // not possible since 'Integer' array can't hold Longs
If you want to add things you should declare your variable 'list' as List<? super MyClass>. This will enable you to add any subclass of 'MyClass' into the list.
List<? super Number> numbers = new ArrayList<Number>();
numbers.add(Long.valueOf(2));
This is known as the 'get' and 'put' principle. If you want to 'get' stuff you need to have the List<? extends MyClass> syntax whilst if you want to 'put' stuff you need to have the List<? super MyClass> syntax. Ofcourse if you need to do both then you need to use List<MyClass> syntax.
Try this:
List<MyClass>
It should work
Define it this way:
List<MyClass> list

Java generic method wildcard mismatch issue

The basic thing I want to achieve is to map a list of references List<Ref<Thing>> to a list of the actual objects, but given by the superclass List<SuperThing>. In this example, Thing extends SuperThing and Ref<Thing> has a method public Thing get() to get the referenced object.
The method that I assumed valid:
public <T> List<T> refsToObjects(List<Ref<? extends T>> list) {
List<T> result = new ArrayList<T>();
for(Ref<? extends T> ref : list) {
result.add(ref.get());
}
return result;
}
But when I try to use it
List<Ref<Thing>> refs;
List<SuperThing> objectList = refsToObjects(refs);
I get this error message: The method refsToObjects(List<Ref<? extends T>>) is not applicable for the arguments (List<Ref<Thing>>)
I did not actively use the ? extends T wildcard structure before, but what am I doing wrong?
It works, if you specify the "extended" parameter also as generic parameter:
public <T, S extends T> List<T> refsToObjects(List<Ref<S>> list) {
List<T> result = new ArrayList<T>();
for(Ref<S> ref : list) {
result.add(ref.get());
}
return result;
}
Declare your method as taking List<? extends Ref<? extends T>> instead:
public <T> List<T> refsToObjects(List<? extends Ref<? extends T>> list) { ... }
Nothing should have to change within the body.
EDIT: type inference still seems to fail at the call site using this solution. It only works with a call like this.<SuperThing>refsToObjects(refs). So wrm's solution using an additional type parameter is preferable if you can expect this kind of usage.
As I can see it, there are two errors in your code. The first is to believe that the type List<Ref<? extends Thing>> is a supertype for List<Ref<Thing>>. If we create a subclass of Thing like DerivedThing, we cannot add an instance of Ref<DerivedThing> to a list of List<Ref<Thing>>:
List<Ref<Thing>> refs = new ArrayList<Ref<Thing>>();
refs.add(new Ref<Thing>()); // OK.
refs.add(new Ref<DerivedThing>()); // Error!
However, if we replace this with List<Ref<? extends Thing>> then there is no more problem with the class DerivedThing:
List<Ref<? extends Thing>> refs = new ArrayList<Ref<? extends Thing>>();
refs.add(new Ref<Thing>()); // Still OK.
refs.add(new Ref<DerivedThing>()); // Now OK!
Therefore, if the compilator was to allow to pass of a value of List<Ref<? extends Thing>> to a function taking List<Ref<? extends Thing>> as its argument, this would allow the function to add some invalid item to the list.
The second error is to think that the base type (or erasure type) of <? extends Thing> is SuperThing instead of remaining Thing. Here, <? extends Thing> designates the collection of type composed of Thing and its derived classes and not the collection of SuperThing and its derived classes. Therefore, we could write:
List<Ref<? extends Thing>> refs = new ArrayList<Ref<? extends Thing>>();
refs.add(new Ref<Thing>());
List<Thing> objectList = refsToObjects(refs);
or, with SuperThing as the erasure type:
List<Ref<? extends SuperThing>> refs = new ArrayList<Ref<? extends SuperThing>>();
refs.add(new Ref<Thing>());
List<SuperThing> objectList = refsToObjects(refs);
but not a combination of both because List<SuperThing> is not a superclass for List<Thing>. Notice that we can stil add a Ref<Thing> to a List<Ref<? extends SuperThing>>. Therefore, use of one of the above solution or use the wrm's solution if you want to keep List<Ref<Thing>> as your starting point.
Personally, I would prefer to use polymorphism at its fullest extend and always reference everything from a SuperThing; even when creating Thing or Ref<Thing> objects. For example, if we add a parameter of type T to the constructor of Ref():
List<Ref<SuperThing>> refs = new ArrayList<Ref<SuperThing>>();
refs.add(new Ref<SuperThing>(new Thing()));
List<SuperThing> objectList = refsToObjects(refs);
Note that we are now passing an object of type Thing to a reference of type SuperThing in the constructor of Ref(). By using the superclass of the hierarchy as the reference for all the derived objects, all the coding become much more simpler. OOP works very well and very easily when you choose to see all the objects mostly only through their superclass and this extends to the use of generic.
The types must match exactly, so:
List<Ref<? extends Thing>> refs = new ArrayList<Ref<? extends Thing>>();
List<SuperThing> objectList = refsToObjects(refs);
should work.
It will also work if you do something like this:
List<Ref<Thing>> refs;
List<SuperThing> objectList = this.<Thing>refsToObjects(refs);
What is happening is that the method expects something which extends T. but you never define T. The one explained by #wrm defines it in the method.

Java ArrayList of ? extends Interface

I have a group of classes that all implement a validation interface which has the method isValid(). I want to put a group of objects--all of different classes--into an ArrayList, loop through them and call isValid() on each.
Here's my code
Email email = new email();
Address address = new Address();
ArrayList<? extends Validation> myValidationObjects = new ArrayList();
But when I try to do:
myValidationObjects.add(email);
I get:
The method add(capture#2-of ? extends Validation) in the type ArrayList
is not applicable for the arguments (Email)
Both Email and Address implement Validation.
According to this document, I should be able to use extends for both interfaces and subclasses.
List<? extends Validation> myValidationObjects
Incorrect reading
"myValidationObjects is list of objects that extend Validation."
Correct reading
"myValidationObjects can be a list of any type that extends Validation. For example, it could be a List<RangeValidation> or a List<RegexValidation>."
Since there is no object you can legitimately add to both a List<RangeValidation> and a List<RegexValidation>, Java prevents you to call add on a variable of such type.
Your case is in fact the simpler one: you need the definite type List<Validation>.
You can use:
List<Validation> myValidationObjects = new ArrayList<>(); // Java 7
List<Validation> myValidationObjects = new ArrayList<Validation>(); // pre Java 7
Now you can add any instance of a class that implements Validation to that list.
The declaration ArrayList<? extends Validation> means a list of an unknown class that extends Validation. Email is not compatible with this unknown class.
You can use ArrayList<Validation> for your list.
If a generic class's T is <? extends Foo>, then the only thing you can pass to a method that takes T is null -- not any subclass that extends Foo.
The reason is that List<? extends Validation> doesn't mean "a list of things that extend Validation". You can get that with just List<Validation>. Instead, it means "a list of some type, such that that type extends Validation."
It's a subtle distinction, but basically the idea is that List<? extends T> is a subtype of List<T>, and you therefore don't want to be able to insert anything into it. Think of this case:
List<FooValidation> foos = new ArrayList<>();
List<? extends Validation> validations = foos; // this is allowed
validations.add(new BarValidation()); // not allowed! this is your question
FooValidation foo = foos.get(0);
If the third line were allowed, then the last line would throw a ClassCastException.

Can all wildcards in Java be replaced by non-wildcard types?

I can't find any example where wildcards can't be replaced by a generic.
For example:
public void dummy(List<? extends MyObject> list);
is equivalent to
public <T> void dummy(List<T extends MyObject> list);
or
public <T> List<? extends T> dummy2(List<? extends T> list);
is equivalent to
public <T, U> List<U extends T> dummy(List<U extends T> list);
So I don't undertand why wildcard was created as generics is already doing the job. Any ideas or comments?
Nope, it is not always replaceable.
List<? extends Reader> foo();
is not equivalent to
<T> List<T extends Reader> foo();
because you don't know the T when calling foo() (and you cannot know what List<T> will foo() return. The same thing happens in your second example, too.
The demonstration of using wildcards can be found in this (my) answer.
An easy answer is that, deeper level wildcards can't be replaced by a type variable
void foo( List<List<?>> arg )
is very different from
<T>
void foo( List<List<T>> arg)
This is because wildcard capture conversion is only applied to 1st level wildcards. Let's talk about these.
Due to extensive capture conversion, in most places, compiler treats wildcards as if they are type variables. Therefore indeed programmer can replace wildcard with type variables in such places, a sort of manual capture conversion.
Since a type variable created by compiler for capture conversion is not accessible to programmer, this has the restricting effect mentioned by #josefx. For example, compiler treats a List<?> object as a List<W> object; since W is internal to compiler, although it has a method add(W item), there's no way for programmer to invoke it, because he has no item of type W. However, if programmer "manually" converts the wildcard to a type variable T, he can have an item with type T and invoke add(T item) on it.
Another rather random case where wildcard can't be replaced type variable:
class Base
List<? extends Number> foo()
class Derived extends Base
List<Integer> foo()
Here, foo() is overriden with a covariant return type, since List<Integer> is a subtype of List<? extends Number. This won't work if the wildcard is replaced with type variable.
There is a usage difference for your examples.
public <T> List<? extends T> dummy2(List<? extends T> list);
returns a list that can contain an unknown subtype of T so you can get objects of type T out of it but can't add to it.
Example T = Number
List<? extends Number> l = new ArrayList<Integer>(Arrays.asList(new Integer(1)));
//Valid since the values are guaranteed to be a subtype of Number
Number o = l.get(0);
//Invalid since we don't know that the valuetype of the list is Integer
l.add(new Integer(1));
So the wildcard can be used to make some operations invalid, this can be used by an API to restrict an interface.
Example:
//Use to remove unliked numbers, thanks to the wildcard
//it is impossible to add a Number
#Override public void removeNumberCallback(List<? extends Number> list){
list.remove(13);
}

Why does ListDataModel not work with a bounded type parameter?

I just tried to create a ListDataModel with a bounded type, like this:
DataModel<? extends Foo> model = new ListDataModel<? extends Foo>(fooList);
, where fooList is of the type List<? extends Foo>. I get the following error:
unexpected type
required: class or interface without bounds
found: ? extends Foo
My current workaround is to copy my data into an ArrayList<Foo>, and build a DataModel<Foo> from that, but I would like to know why this is necessary, and if there is any way to make it work?
<? extends Foo> means "some type, I don't know which one, which is or extends Foo". When constructing the data model, you need to tell him which type of data it contains, not just one unknown type.
Just construct your data model like this:
DataModel<? extends Foo> model = new ListDataModel<Foo>(fooList);
Unfortunately, the ListDataModel<Foo> constructor only accepts a List<Foo>, and not a List<? extends Foo>. It seems to me like a misconception. For example the HashSet<E> constructor takes a Collection<? extends E> as argument. If you accept the type safety warning, just casting your list to a List<Foo> should work.
Since ListDataModel is read only, it is wrong for its constructor to accept only List<E>. It's ok to cast to bypass this design flaw.
A more general question: suppose ListDataModel is writable, what now?
If fooList is a List<? extends Foo>, then it certainly is a List<W> for a concrete W which extends Foo. Then we should be able to new ListDataModel<W>(fooList), the result type is a DataModel<W>, which is assignable to DataModel<? extends Foo> model.
This is how compiler internally reason about wildcards (wildcard capture); too bad we can't access W directly in Java (it's a so called non-denotable type), but we can cause wildcard capture through method invocation:
static <W> ListDataModel<W> make(List<W> list)
{
return new ListDataModel<W>(list);
}
List<? extends Foo> fooList = ...;
DataModel<? extends Foo> model = make( fooList );
When compiling make( fooList ), compiler internally refines the type of fooList to a List<W> where <W extends Foo>; then the rest works natually.
In Java 7, type inference is extended to constructors with <> syntax
List<? extends Foo> fooList = ...;
DataModel<? extends Foo> model = new ListDataModel<>(fooList); // OK in Java 7
With <>, constructor call is pretty much the same as method calls; so make() is no longer needed. Prior to Java 7, static factory methods like make() were needed to amend the problem that constructors don't do inference. That practice is now obsolete.

Categories

Resources