I know that in order to create an object of ArrayList type I need to:
ArrayList<MyType> l = new ArrayList<MyType>();
I know it because I've seen this in books.
But looking at Java SE API for the ArrayList constructor I see:
ArrayList(Collection<? extends E> c)
I don't understand, how would I ever get an idea to specify the type of objects my new ArrayList object would hold? How would I know from this definition that I have to specify <MyType> during instantiation and variable declaration?
That method is meant to copy an existing collection instance to the new ArrayList not to create on from scratch. The elements of the collection it will accept have an upper bound of type E which will be the type of your new ArrayList then.
In order to find out if you need type parameters in your declaration, you don't look at the constructor, you look at the class definition itself. The arguments of the constructors have nothing to do with the type of the object itself - a constructor can take any kind of arguments, just like any other method. Here's the class definition:
public class ArrayList<E>
extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, Serializable
The class ArrayList<E> part says that there is a class called ArrayList and it requires one type parameter (which is called E here).
Let's say you have a set of shape classes. The base class is Shape, and the two implementations are Square and Circle. Now, after you've done some specific setup for the list of objects you wanted, you need to add it all together to send it to a rendering function. The code below should make what I'm describing a little more clear:
ArrayList<Square> squares = readSquares();
ArrayList<Circle> circles = readCircles();
ArrayList<Shape> queue= new ArrayList<Shape>(squares);
queue.addAll(circles);
renderShapes(queue);
The ArrayList(Collection<? extends E> c) constructor makes line five possible. With generics, the processor is not smart enough to automatically determine that Square extends Shape by default. You have to tell the compiler that is what you intended. The way you do that is with the wildcard <? extends E>. Now the compiler will make sure that every object added is at least a Shape.
That's always true when you construct an object for a class that takes a generic type. If you scroll up to the top, you'll see Class ArrayList< E >. That's the hint.
Related
I'm working with superclass and subclass. My subclass extends the SuperClass this way:
public class SubClass extends LinkedList<SuperClass>
Does this mean that my subclass hold LinkedList properties. For example, if I'm creating objects of my subclass from other classes, am I able to do something like:
public class another {
private SubClass object;
public another(ArrayList<String> list) {
object = new SubClass();
SubClass localObject = object;
for (String elements : list) {
localObject.addAll(elements);
}
}
}
I want to add elements to end of the linked list.
Would this work? Or would I have to create add(), addAll() etc methods in the subclass in order for it to work?
SubClass inherits all the methods of LinkedList, but since it extends LinkedList<SuperClass> it can contain only objects of type SuperClass.
Your code at
for (String elements : list) {
localObject.addAll(elements);
has two problems.
The method addAll takes a Collection<? extends T>, which in your case is Collection<SuperClass> but you are passing in a single String.
Even if you modified it to pass in a collection of String it would still fail to compile. Only a Collection<SuperClass> is acceptable here.
If you changed it to
for (String element : list) {
localObject.add(element);
i.e. change to the add method instead of addAll, this is still wrong because the add() method in SubClass only takes objects of type SuperClass.
Jim Garrison is correct. But let me make it concrete.
The class called Float extends the class called Number
Integer is another class that also extends Number.
If you create a list of numbers (aka List<Number>) then you can put floating point numbers in that list, and you can put integers in that list.
The following code will compile cleanly.
List<Number> numberList = new LinkedList<Number>();
numberList.add(new Float(1.5));
numberList.add(new Integer(1));
You can not, however, add a potato to a list of numbers. Nor can you add a String, or any other type of object which is not a Number (or subclass of Number). The following code will generate the compile time error you cited.
list.add("this is a String.");
Getting back to your code now.
First let me say that when you extend List<SuperClass>, this gets you another List<SuperClass> with a different name, Subclass in this case. Which is to say SubClass is a list containing SuperClass objects. You could add some additional methods to your newly named class to differentiated it from List<SuperClass> but first and foremost it is just a list containing instances of SuperClass. I think you knew that already but I want to be clear.
Your method called another accepts an argument that is a list of strings (List<String>). So that means that every member of that list, is a String. But you are trying to pulls stuff out of that list (of strings) and then add it to a list of SuperClass. But the list of SuperClass can ONLY accept instances of SuperClass (whatever that is). So unless SuperClass happens to be a super class of String (and String only extends Object), then you will get an error.
I'm trying to learn how to use generics and for that, I'm creating my own data structure similar to a list with an Iterator and all.
The uppermost class is declared as:
public class BasicList<A> implements Iterable {
}
It's just a list with nodes and a single pointer to the next element.
There's another list, called DescendingList, that does pretty much the same, only this time around, you're getting another output since the Iterator is different. I'm giving a comparator to this List to get the Iterator to work. The comparator is just an interface:
public interface Bigger<A> {
boolean bigger(A x);
}
DescendingList looks like this:
public class DescendingList<A extends Bigger<A>> extends BasicList<A> implements Iterable {
}
The idea is that it could work with any kind of object that is comparable through the Bigger interface.
Now I have an abstract class:
public abstract class Rock implements Bigger<Rock> {
}
And finally, a normal class which extends the Rock class:
public class Mineral extends Rock {
}
So, the issue at hand is that, while I can easily create a new BasicList filled with Minerals like this:
BasicList<Mineral> min = new BasicList<Mineral>();
I can't do the same with the DescendingList. Whenever I try going with
DescendingList<Mineral> min = new DescendingList<Mineral>();
my IDE (IntelliJ) goes all
"Type parameter "Rock" is not within it's bound; should implement Bigger<Mineral>"
And I don't really get why that happens. I am pretty sure that I've messed up the type parameters somehow and I'm really not sure where. This should be solveable somehow (at least without removing any classes/interfaces - the class headers might be and probably are completely messed up). Thanks in advance for any help you might offer.
A Mineral isn't a Bigger<Mineral>, it's a Bigger<Rock>, and that is incompatible with Bigger<Mineral>, because even though a Mineral is a Rock, Java's generics are invariant. It needs to be a Bigger<Rock> because of how Rock is defined -- implementing Bigger<Rock> -- and that DescendingList declares type parameter A to be a Bigger<A>.
Because it's a consumer (type parameter as a method parameter), the type parameter A needs a lower-bound (super) on its declaration.
class DescendingList<A extends Bigger<? super A>> // ...
This way Mineral will be within its own bound.
As an aside, you are implementing the raw form of Iterable in BasicList; you should supply a type argument there.
Should you use
List<ParentClass> foo = new ArrayList<ParentClass>
foo.add(ChildClassObject)
or
List<? extends ParentClass> bar - new ArrayList<ParentClass>
bar.add(ChildClassObject)
Also, could someone explain more to me the latter parameterization of List?
Use the first approach. The second doesn't allow you to add any element. This is because the second approach uses an unknown type that extends ParentClass and cannot assure that the elements to be stored are from this type.
More info:
Java Generic with ArrayList <? extends A> add element
List<? extends ParentClass> is a list of some specific subtype of ParentClass. Since, it's not know what exact specific subtype it is, the compiler won't let you add any object to it.
For starters, it's easy to mistake it with any subtype of ParentClass and wonder why they can't add the instances of the subtypes.
For instance, suppose you have a following class hierarchy -
class P { }
class A extends P { }
class B extends P { }
Now if you declare a a list as follows -
List<? extends P> l = ...;
it means l is a list of some specific subtype of P, which could be either A or B (or even P itself), but the compiler doesn't know exactly which one. So, you can't add anything to it, because the chance is that you could be adding an instance of B to a list of As creating some heap pollution.
You would declare such a list when you are only interested in reading the elements of list as P's instances.
Say, for example, that I have
public class Foo<Game> extends ArrayList<Game>{
}
and I want to iterate over its elements. I'm aware that I can use a for loop with an its iterator. But trying something like:
for(Game g : super)
does not seem to work. This is mysterious(at least to me) because super is a reference to an object of type ArrayList<Game> which should be able to be iterated over. Is there some syntax that I'm missing?
EDIT: I added the Foo type to the class declaration.
This is mysterious(at least to me) because super is a reference to an object of type ArrayList<Game> [...]
super is not an object. If you instantiate a class that inherits from another class, you don't get two objects (this and super), there is only the one instance. (Heck, it'd become unwieldy with deep inheritance hierarchies!) super allows you to access methods from the super class (again, not an object) and use them in your method implementations. Those super methods still execute in the context of the this object, but now that object is of a subclass type. That's perfectly fine though, and it's one of the foundations for polymorphism.
this is an object: it's an instance of Foo and, because Foo inherits from ArrayList<Game>, it is also an ArrayList<Game> and thus implements Iterable<Game>. Therefore, you can just iterate over this.
Thus, you're looking for:
for (Game g : this) { ... }
I am a little confused with something.
I have a class where its not a collection, but it does refer to generic objects:
public class XClass<E extends AnInterface>{
E instanceobject;
public void add(E toAdd){}
}
public interface AnInterface{}
public class A implements AnInterface{}
public class B implements AnInterface{}
I believe I read somewhere that <? extends AnInterface> is to be used (when declaring an instance of XClass) if you want multiple subtype-types in the generic object at the same time, whereas <T extends AnInterface> would only allow you to have a single type of subtype in the generic class at once?
However, I can just use:
XClass<AnInterface> xc = new XClass<AnInterface>();
A a = new A();
B b = new B();
xc.add(a);
xc.add(b);
and this way I can pass in multiple subtypes of Supertype to the generic class......
I am not seeing the purpose of using "?" and is there anything wrong with using the Interface as the generic parameter?
The reason why you can add objects of both type A and B is due to the fact that you parametized your XClass with the interface, so there is nothing wrong with adding two different classes that implement that interface.
If, on the other hand, you had defined XClass as:
XClass<A> xc = new XClass<A>();
then the expression xc.add(b); would give a compilation error, since all the objects added must have the same type as was declared, in this case, A.
If you declare you xc as, for instance:
XClass<? extends AnInterface> xc = new XClass<AnInterface>();
Then it's not legal anymore to add a or b, since the only thing we know is that xc is of some unknown but fixed subtype of AnInterface, and there is no way to know if that unknown type is A or B or anything else.
But let's say you're writing a method to accept a XClass type that you can iterate over the elements that were added before. Your only restriction (for the sake of the example), is that the items extend AnInterface, you don't care what the actual type is.
You can declare this method like:
public static void dummyMethod(XClass<? extends AnInterface> dummy){
//do stuff here, all the elements extend (implement in this case), AnInterface, go wild.
}
And now you can pass into this method anything like XClass<A>, XClass<B> or XClass<AnInterface>, and it will all be valid.
Keep in mind that you can't add to the object you pass, for the same reason above. We don't know what the unknown type is!
public static void dummyMethod(XClass<? extends AnInterface> dummy){
//do stuff here, all the elements extend (implement in this case), AnInterface, go wild.
dummy.add(new A()); //you can't do this, we have no idea what type ? stand for in this case
}
You can use E if you want to have an instance of XClass to use only one subclass of AnInterface and no other Classes implementing AnInterface that do not extend / implement E.
For example given
public class ClassOne implements AnInterface {} and public class ClassTwo implements AnInterface {}
If you were to use
public class XClass<E extends AnInterface> and <ClassOne>XClass xc = new <ClassOne>XClass() then you can only use an object of ClassOne in your add method not one of ClassTwo. Using ? would allow you to pass in any class implementing AnInterface, either ClassOne or ClassTwo.
Using Identifier E means "For this object I want to use type E and any subclasses", using ? means "I want to use any type that matches the the expression"
In your example you need type erasure in the method "add", so you should't use wildcards in your class.
Wildcards are only to be used when you do not need type erasure (i.e. you don't care about the type as long as it is a subclass of..) and also when you will need to subtype the generics itself.
The wildcard simply means that it will be some class that meets that criteria. So ? extends AnInterface means it will be one (and only one) class that extends AnInterface.
So it could be:
XClass<Impl1>
XClass<Impl2>
etc...
However, at runtime, you don't know what that class will be. For this reason calling methods which take the actual type as a parameter is inherently unsafe, since it's impossible for the compiler to know if the parameter is appropriate for the actual instantiated instance.
Take lists as an example. Something might be declared like this:
List<? extends Number> list = new ArrayList<Integer>();
What would happen if you try to do either of these:
list.add(new Double(0));
list.add((Number) new Long(1L));
It would not compile, because the generic parameter type is unknown at compile time. So the compiler can't tell if Double or Number would be appropriate to pass to the actual instance (in this case ArrayList<Integer>). This is when you get the infamous capture-of compile error.
This, however is permissible, since you know for certain at compile time that the list can take any instance of Number (which includes subclasses).
List<Number> list = new ArrayList<Number>();
list.add(new Double(0));
list.add((Number) new Long(1L));