Java ArrayList of ? extends Interface - java

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.

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

Generic extend can't add the same type

I'm new to these generic types. In the below code, I created a method that accepts a List of items that extends "String".
My Question? - When the list can be assigned with a new list that is , why can't a string "test" can be added...It gives me a compilation error.
public class Child {
public void takeList(List<? extends String> list){
list = new ArrayList<String>();
list.add("test");
}
}
Because it's not the runtime type that's relevant here. list is still of type List<? extends String>, you've just happened to assign it to a new ArrayList<String>(). Consider this:
list = rand() ? new ArrayList<String>() : new ArrayList<NotString>();
The compiler could not possibly tell if list.add("test") will be valid -- it only makes decisions based on the compile-time type of list.
Note that in reality nothing extends String, it's a final class.
There's a subtle difference. It takes a list that contains one type of thing (a thing that extends string). This list may be a subclass of String and therefore not be a String iyswim. See http://docs.oracle.com/javase/tutorial/java/generics/upperBounded.html Upper bounded wildcards.
If it was
public void takeList(List<? *super* String> list){
Then you could add strings to it, because the list is guaranteed to be able to accept Strings.
When you have a variable with a wildcard, and a method that takes the generic type parameter, Java cannot ensure type safety. It must disallow this call.
Consider a List<? extends Animal> for example. You may have assigned it List<Dog>, but the variable could be assigned a List<Squid>. You shouldn't be allowed to add a Dog to such a list.
To allow the add method to be called, remove the wildcard.
public void takeList(List<String> list){
Besides, String is final, so there really is no point to saying ? extends String.

how to make method parameter type ArrayList<Object> take different object types

I have an abstract method as part of an abstract class with the following declaration:
abstract public ArrayList<Device> returnDevices(ArrayList<Object> scanResult);
I want the parameter that is passed to be an ArrayList, but the type object in the ArrayList will be dependent on the child class that inherits this superclass and implements the method returnDevices.
I thought that I could achieve this by making the method abstract as above, and then in the child class that inherits it do something like:
public ArrayList<Device> returnDevices(ArrayList<Object> scanResult) {
Iterator<Object> results = scanResult.iterator();
while(results.hasNext())
Packet pkt = (Packet) results.next(); // HERE: I cast the Object
}
That is fine and does not cause an error, but when I try to call returnDevices by using a parameter of type ArrayList<Packet>, like the following:
ArrayList<Packet> packets = new ArrayList<Packet>();
// <----- the "packets" ArrayList is filled here
ArrayList<Device> devices = returnDevices(packets);
... I get the error:
The method returnDevices(ArrayList<Object>) in the type ScanResultParser is not applicable for the arguments (ArrayList<Packet>)
So clearly it is rejecting the parameter type. What is the proper way to achieve what I am trying to do?
- This is how Collections are made type-safed in Java so a wrong type doesn't enter into the Collection, As collection are checked only during the `Compilation time and Not during the Runtime.. ie a Cat object should not enter into a Collection of type Dog.
You can do it this way...
public ArrayList<Device> returnDevices(ArrayList<? extends Object> scanResult)
Or
public <T extends Object> ArrayList<Device> returnDevices(ArrayList<T> scanResult)
The problem is, even though Packet is-a Object, ArrayList<Packet> is not a ArrayList<Object>. Generic type is invariant in Java.
You can use the extends and super keywords with your type parameters to introduce that type of effect in Java generics.
I think the following is what it should be
abstract public <T extends Device> List<T> returnDevices(List<T> scanResult);
With that the method with either take a list of Devices and return alist of Devices or take a list of some subtype of Device and return a list of the same subtype.
Side note: Coding against some concrete type (e.g ArrayList) is not a good practice. You should always code against the interface whenever possible (e.g. List).

another java generic question

I have the following class:
interface Able{/* ... */}
class A implements Able{/* ... */}
and I have
Map<String,? extends Able> as;
as = new HashMap<String, A>();
why does the following cause an error:
as.put("a", new A());
Any ideas?
The reference to java generics is good (jdk site).
Indeed #Oli_Charlesworth gave a good answer, but maybe this one will be more complete.
In a Collection<? extends Able> you can't insert anything that's right.
If you have
class A implements Able {...}
and
class B implement Able {...}
Then, Collection<? extends Able> is a super type of both :
Collection<A>
Collection<B>
Thus it is legal to write some statement like
//Code snippet 01
Collection< ? extends Able > list;
Collection<A> listA;
Collection<B> listB;
list = listA;
list = listB;
That is indeed the reason why the wildcard notation Collection<? extends Able> exists.
But, here things are getting more interesting :
In a Collection<A> you can only insert objects that are A (including subclasses). Same for Collection<B>. In both you can't add something that is just Able. For instance :
//Code snippet 02
listA.add( new A() ); //valid at compile-time
listA.add( new B() ); //not valid at compile-time
listB.add( new B() ); //valid at compile-time
listB.add( new A() ); //not valid at compile-time
Thus, if you group what we saw in code snippets 01 & 02, you will understand that it's absolutely impossible for the compiler to accept a statement like :
Collection< ? extends Able > list;
list.add( new A() ); //not allowed, will work only if list is List<A>
list.add( new B() ); //not allowed, will work only if list is List<B>
So yes, the super type Collection< ? extends Able > doesn't accept to add anything. More general types offer the intersection of functionalities of subtypes, and, as such, less features that subtype. Here, we lose the ability to add A objects and B objects. Those feature will happen later in the hierarchy... and it even means that we can't add anything in the super class Collection< ? extends Able >
Additional remark :
Also, note that in a Collection<Able> you can add whatever you want like this :
Collection< Able > list;
list.add( new A() ); //valid
list.add( new B() ); //valid
But, Collection<Able> is not a superclass of Collection<A> and Collection<B>. It would mean, as with any inheritance relation, that subclasses can do whatever their superclass can do, as inheritance is specialization. So, this would mean that we could add A objects and B objects to both subclasses Collection<A> and Collection<B> and that is not the case. So as it's not a superclass you can't have :
Collection<Able> list;
Collection<A> listA;
Collection<B> listB;
list = listA; //not valid because there is no inheritance hierarchy
list = listB; //not valid because there is no inheritance hierarchy
Note that inheritance is a hyperonimic relation (generalization/specialization) and collections define a meronimic relation (container/containee). And it's a headache to combine both of them formally, even though it's somewhat used quite easily by the fuzzy creatures humans are, for instance in the french figure of speech : synecdocque. :)
From http://download.oracle.com/javase/tutorial/extra/generics/wildcards.html:
There is, as usual, a price to be paid
for the flexibility of using
wildcards. That price is that it is
now illegal to write into [a wildcard-based container]. For instance,
this is not allowed:
public void addRectangle(List<? extends Shape> shapes) {
shapes.add(0, new Rectangle()); // Compile-time error!
}
You should be able to figure out why
the code above is disallowed. The type
of the second parameter to
shapes.add() is ? extends Shape-- an
unknown subtype of Shape. Since we
don't know what type it is, we don't
know if it is a supertype of
Rectangle; it might or might not be
such a supertype, so it isn't safe to
pass a Rectangle there.
A good way to understand the issue is to read what the wildcard means:
Map<String,? extends Able> as;
"A map with keys of type String and values of one type that extends Able."
The reason why add operations are not allowed is because they "open the door" to introduce different types in the collection, which would conflict with the typing system.
e.g.
class UnAble implements Able;
Map<String,UnAble> unableMap = new HashMap<String,UnAble>();
Map<String,? extends Able> ableMap = unableMap;
ableMap.put("wontwork",new A()); // type mismatch: insert an A-type into an Unable map
A correct use of the the wildcard construction would be:
Result processAble(Map<String,? extends Able>) { ... read records & do something ... }
Map<String,A> ableMap = new HashMap<String,A>;
ableMap.put("willwork",new A());
processAble(as);
processAble(unableMap); // from the definition above
declaration of
Map<String,? extends Able> as;
means "any map, with String keys, and values being subtype of Able". So, for example, you can do following:
Map<String,? extends Able> as = new HashMap<String, SubSubAble>();
And now let's look at this example:
Map<String,? extends Able> as = new HashMap<String, SubSubAble>();
as.put("key", new A() );
If it were correct, you'll finish having HashMap with content {"key", new A()} - which is type-error!
Collection<?> is the supertype for all kinds of collection. It is not a collection that can hold any type. At least that was my misunderstanding of the whole concept.
We can use it where we don't care about the generic type, like in this example:
public static void print(Collection<?> aCollection) {
for (Object o:aCollection) {
System.out.println(o);
}
}
If we had chosen the signature instead:
public static void print(Collection<Object> aCollection)
we would have limited ourselves to collections of type Collection<Object> - in other words, such a method wouldn't accept a Collection<String> type value.
So a Collection<?> type is not a collection that can take any type. It only takes the unknown type. And as we don't know that type (its unknown ;) ), we can never add a value, because no type in java is a subclass of the unknown type.
If we add bounds (like <? extends Able>), the type is still unknown.
You're looking for a declaration of a map, whose values all implement the Able interface. The correct declaration is simply:
Map<String, Able> map;
Let's assume we have two types A and B that subclass Able and two additional maps
Map<String, A> aMap;
Map<String, B> bMap;
and want a, method that returns any map whose values implement the Able interface: then we use the wildcard:
public Map<String, ? extends Able> createAorBMap(boolean flag) {
return flag ? aMap: bMap;
}
(again with the constraint, that we can't add new key/value pairs to the map that is returned by this method).
You can't insert any object of any type in a collection declared using the wild card '?'
you can only insert “null”
Once you declare a collection as List, the compiler can't know that it's safe to add a SubAble.
What if an Collection<SubSubAble> had been assigned to Collection<Able>? That would be a valid assignment, but adding a SubAble would pollute the collection.
How can elements be added to a wildcard generic collection?

Java: Declaring variables with multiple generic "constraints" on them

I have a bunch of classes in java that all implement an interface called IdObject (specifying a getId() method). Moreover, they also all implement Comparable<> with themselves as type parameter, so they are all comparable to themselves.
What I'd like to do is declare a list of such objects, fill it, then sort it and call getId() on them. So my code looks like this:
List<? extends IdObject & Comparable<?>> objectList = null;
if (foo) {
objectList = new ArrayList<TypeA>();
...
} else if (bar) {
objectList = new ArrayList<TypeB>();
...
}
if (objectList != null) {
Collections.sort(objectList);
for (IdObject o : objectList) {
System.out.println(o.getId());
}
}
Basically my problem lies in the first line -- I want to specify two "constraints" for the type because I need the first one to make sure I can print the ID in the loop and the second one to make sure I can use Collections.sort() on the list.
The first line does not compile.
Is there a way to make this work without specifying a generic type without type parameters and using unchecked operations? I could also not find an example of this on the internet.
Greetings
List<? extends IdObject & Comparable<?>>
This type of multiple bounded type parameters is only possible in class and method signatures, I'm afraid.
So I guess the closest you can get is to define an interface:
public interface MyInterface extends IdObject, Comparable<MyInterface>
And declare your list like this:
List<? extends MyInterface> objectList = null;
How about creating antoher type ;
public CompIdObject extends IdObject implements Comparable {
...
}
and use this type for your generics ?
List<? extends CompIdObject<?>> objectList = null;
I've wanted to do similar things before on fields as well, but it can't be done (perhaps someone could elaborate on whether this is a technical restriction or a design choice, I'm not sure.)
The way forward would be to design an interface that extends the IdObject and Comparable, then use that in your generic list definition.

Categories

Resources