I've been trying to extend the ArrayList class without much success. I want to extend it, and be able to parameterize it.
So normally you have something like
ArrayList<SomeObject> list = new ArrayList<SomeObject>();
I want
MyList<SomeObject> list = new MyList<SomeObject>();
Simply extending ArrayList doesn't work.
public class MyList extends ArrayList ...
The when I try to use it I get the error
The type MyList is not generic; it
cannot be parameterized with arguments
<SomeObject>
I've tried variations of
public class MyList extends ArrayList<Object>
public class MyList<SubObject> extends ArrayList<Object>
with no success, If I use the subobject behind the class name it appears to work, but hides methods in the subobject class for some reason.
Any thoughts or suggestions on how to get this working right are appreciated.
You need to specify a type for the ArrayList's type parameter. For generic type parameters, T is fairly common. Since the compiler doesn't know what a T is, you need to add a type parameter to MyList that can have the type passed in. Thus, you get:
public class MyList<T> extends ArrayList<T>
Additionally, you may want to consider implementing List and delegating to an ArrayList, rather than inheriting from ArrayList. "Favor object composition over class inheritance. [Design Patterns pg. 20]"
public class MyList<T>
extends ArrayList<T>
{
}
MyList<SomeObject> list = new MyList<SomeObject>();
or
public class MyList
extends ArrayList<SomeObject>
{
}
MyList list = new MyList();
You shouldn't extend ArrayList, extend AbstractList instead:
public class MyList<T> extends AbstractList<T> {
public int size() {...}
public T get(int index) {...}
}
Related
I have a class
public abstract class superObject{}
and subclasses
public class subObj1 extends superObject{}
public class subObj2 extends superObject{}
In my main class I have
List<superObject> aList = new ArrayList<>();
of which will allow me to invoke
aList.add(subObj1)
but I cannot invoke
aList.get(0)
becuase for any object in the Array list that isn't of type superObject the compilier says I'm trying to return incompatiable types.
And I'm completely baffeled as to why?
edit: example code was incorrect
Take a look at the next code:
ArrayList<? extends Object> list = new ArrayList<Object>();
list.add(new Object());
It does not compile. The question is why?
ArrayList<? extends Object>
is the same as just
ArrayList<?>
and you can assign any ArrayList to a variable of this type. For example,
ArrayList<? extends Object> list = new ArrayList<String>();
is legal. Clearly, the language semantics will not let you add an Object to such a list. In fact, they won't let you add anything at all to it, except null, or something which you directly retrieved from the list.
As noted by Lukas in the comment, it is far from trivial to even add the list's own item back to it: you need a helper method to capture the wildcard into a named type.
public static void main(String[] args) {
ArrayList<? extends Object> list = new ArrayList<String>();
addOwn(list);
}
static <T> void addOwn(List<T> l) { l.add(l.get(0)); }
The problem is that Foo extends Object does not mean Collection<Foo> can be treated as a subtype of Collection<Object>. This is simply because the former class does not permit you to do everything the latter does; for instance, you cannot add an Object to a Collection<Foo>. Using generics instead of some concrete class Foo doesn't change this.
I think the reason is because Generic types are not polymorphic. When you use wildcards ? with extends you cant add anything in the collection except null.
Here is an example to what will happen if that is allowed:
Class Car{
}
class A extends Car {
}
class B extends Car{
}
Now you have List<? extends Car>
public void someMethod(List<? extends Car> list){
list.add(new A()); //this is not valid
}
Also you may invoke the method like this:
List<B> someList = new Array:list<B>();
somemethod(someList);
I have 3 simple classes as follows:
public class ElementA {}
public class ElementB extends ElementA {}
public class ElementC extends ElementB {}
Then if I want to create, for example, generic List which takes only subclasses of ElementA class I can declare it as:
List<? super ElementA> list = new ArrayList<>();
and then use it as follows:
list.add(new ElementA());
list.add(new ElementB());
list.add(new ElementC());
which is fine and can be compiled without errors. But I became confused if I want to store anything but not ElementC or ElementB or ElementA. I declare such List as follows:
List<? extends ElementC> list = new ArrayList<>();
and I can't use it at all because it can store only null values. Same thing happen when I declare List as (notice that I'm using class which is 'in the middle of family'):
List<? extends ElementB>
Why so?
The problem is that the value of ? is not known at runtime. You have to substitute a concrete class/interface in order to be able to do what you want.
If you do this:
List<ElementA> list = new ArrayList<ElementA>();
you are fine since ElementB is an ElementA at the same time. Same stands for ElementC.
List<? extends ElementA> makes sense if you for example declare it in a class and in a subclass you can substitute something concrete as the type parameter. Clumsy example:
public class SomeClass<T> {
private List<? extends T> list;
public void setList(List<? extends T> list) {
this.list = list;
}
}
public class SomeConcreteClass extends SomeClass<Integer> {
public void doSomething() {
List<Integer> list = new ArrayList<Integer>();
setList(list);
}
}
List<ElementA> accepts instances of ElementA, ElementB, and Element C.
List<ElementB> accepts instances of ElementB and Element C.
List<ElementC> accepts instances of ElementC.
There is no reason for the wildcard in your examples.
List<? super ElementA> means a List of some type which is ElementA or a superclass.
List<? extends ElementB> means a List of some type which is a subclass of ElementB. If you get an element it will be ElementB or a subclass, but it doesn't know what the class is, so it can't be sure the element you add is of the right type, since it is unknown (though it does know it to be a subclass of ElementB).
There are uses for wildcard, but your example is not one of them.
You create a List like this
List<? extends ElementC> list = new ArrayList<>();
but let's say, because it's still valid that you got the List like this
List<? extends ElementC> list = getElementCSubclassList(); // declared as returning a `List<ElementCSubclass>`
Now the compiler cannot know that your list object contains ElementCSubclass objects, it can only be sure that it contains some type of ElementC. As such, it can't let you use any methods that expect the actual generic type.
Imagine
public class ElementCSubclass1 extends ElementC {}
public class ElementCSubclass2 extends ElementC {}
...
List<? extends ElementC> list = getElementCSubclass1List(); // declared as returning a `List<ElementCSubclass1>`
list.add(new ElementCSubclass2()); // this would immediately have to fail
Compiler does this so that the previous situation never occurs.
This thing has me stumped. I have a class as follows:
public class SpecialList implements List<MyType> {
// overriden methods
}
Now I have the following method contract to respect in a higher class:
public class MyClass {
private List<SpecialList> bigList = new ArrayList<SpecialList>();
public void doStuff(List<MyType> list)
{
bigList.add((SpecialList)list); // does not compile - invalid cast
}
}
I really am not sure what I am doing wrong here. I have a class that implements the List<MyType> interface, yet I can't cast that class to a List<MyType>? That doesn't make any sense to me.
I am lost. What am I supposed to do to make this work? I suppose this has something to do with generics covariance but at this point I don't know what is wrong here. Can someone point in the right direction? Thanks.
not every List<MyType> (Animal) is MyList (Cow)
you are adding animals to list of cows
I would suggest some parameters :
public class MyClass{
private List<List<MyType>> bigList = new ArrayList<List<MyType>>();
public <E extends List<MyType>> void doStuff(E list)
{
bigList.add(list);
}
}
When you retrieve an element from your bigList however you cannot specialize the element as it comes from a generic list.
If you absolutely need to cast it, maybe your class architecture is not correct.
Or you could abusively do this :
public class MyClass{
private List<List<MyType>> bigList = new ArrayList<List<MyType>>();
public <E extends List<MyType>> void doStuff(E list)
{
bigList.add(list);
}
public <E extends List<MyType>> E getStuff(Class<E> myType,int i)
{
List<MyType> obj = bigList.get(i);
if(myType.isInstance(obj)) return (E) obj;
throw new SomeErrorHere("invalid type for index");
}
}
You defined List<MyList> (i.e. list of MyList). This means that you can add there instances of MyList only. If you are using addAll() you can add list of MyList. But you are trying to add List<MyType>. MyType is definitely not MyList.
And you obviously cannot cast List to MyList.
From what I recall the proper way to typecast list is with the use of generics. Something like:
bigList.add((List<MyList>)(List<?>)list);
However I am not sure of the theory behind this code.
Why is this error occurring
The code is formally correct. You can cast almost any object to any other object and the code will compile. If the cast is invalid, there will be a runtime ClassCastException thrown.
Your IDE can detect unsure casts and complain about them during compile time. Either as a warning or as an error. It is a matter of configuration. Apparently OPs IDE is configured to make such code smells a compile error
Why is this cast unsafe
You can answer your question by answering this:
Can you create a List<MyType> that is not a SpecialList?
You can not cast a List<MyType> to SpecialList because there may be objects which will be List<MyType> and will really not be SpecialList.
Solutions
Change your app architecture
There are two things you can do - either use the class SpecialList all accross your code, or use the generic List<MyType>.
In other words, either change:
doStuff(List<MyType> list) to doStuff(SpecialList list)
or change the
private List<SpecialList> bigList to private List<List<MyType>> bigList
You have to decide whether you want a generic interface list or your own class used everywhere. Remember, that you can alaways cast SpecialList to List<MyType>, because all SpecialList instances are also instances of List<MyType>. It does not work the other way around.
Make sure the cast will always be valid
If you absolutely HAVE TO make this design work, use instanceof to check if the list is really a SpecialList. Like that:
public void doStuff(List<MyType> list)
{
if (list instanceof SpecialList) {
bigList.add((SpecialList)list);
} else {
SpecialList sl = new SpecialList(list); // I hope you have that constructor
bigList.add(sl);
}
}
This works in my Eclipse , this is class SpecialList , Hello is MyType kind of class
package a;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
public class SpecialList implements List<Hello> {
//Overridden methods
}
This one is Myclass
package a;
import java.util.ArrayList;
import java.util.List;
public class MyClass {
private List<SpecialList> bigList = new ArrayList<SpecialList>();
public void doStuff(List<Hello> list)
{
bigList.add((SpecialList)list); //compiles good
}
public static void main(String[] args) {
new MyClass().doStuff(null);
}
}
No compile time error or Runtime Exception
List <MyList> is not same as List <MyType>.
Lets make it simple ,look at the next few lines :
*List<MyList> myList = new ArrayList<MyList>(); //1
myList.add(new MyType);//2 ......Compile ERROR*
If you try to add MyType instance into List<MyList> it will give ERROR.
Why :
Generics means parameterized type.
Generic adds the TYPE SAFETY.
That means ,With generic all cast are automatic and implicit ,
they dont require typecasting while adding and retriving the object
from list explictly.
Real Time Scenario:
If the department of motor vehicles supplies a list of drivers.
We think that a List<Driver> is a List<Person>,assuming that Driver is a subtype of Person.
If that the case , We could add new people who are NOT drivers into the list.
That is All Person are NOT Drivers .
Solution to above problem :
You can use Wildcards with Generics .
Check this link from doc http://docs.oracle.com/javase/tutorial/extra/generics/wildcards.html
I just wonder what usage the following code has:
public class Sub extends java.util.ArrayList<String> {...}
There is no any compiling restriction on the generic constraint java.util.ArrayList<String>.
The compiler does place restrictions on other code based on the type parameter in this case.
This will compile
public class Sub extends java.util.ArrayList<String> {
void addTwice(String s) { this.add(s); this.add(s); }
}
but this will not
public class Sub extends java.util.ArrayList<String> {
void addTwice(Object x) { this.add(x); this.add(x); }
}
Let's say you were making an index for a book, but you don't know how many indices you will need. You could make a class BookIndex extends ArrayList<String> or if you want to get really picky: BookIndex extends ArrayList<IndexEntry>.
/e1
Also, when a one Class extends a generic Class like ArrayList<String> you can grab the String out from the generic declaration, unlike if you had a class ArrayList<T>. In ArrayList<T> you would never be able to figure out what the T is.
You can extend class ArrayList, but it is not something that you should normally do.
Only ever say "extends" when you can truthfully say "this class IS-A that class."
Remember, Its not a good practise to extend the standard classes
Why not use like this ?
public class Sub {
List<String> s = new ArrayList<String>();
// ..
// ...
}
If you do that you can add to the basic functionality of an ArrayList or even change its normal functionality.
For example, you can override the add() method so that it will only add emails to the list.