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
Related
I am new to generics. If I have already created a generic interface IList. But I want to create a method that only works on a list of Students(Student is also another class I created for that problem). Where should I put this method.
P.S. I tried to put this method inside IList class but that doesn't compile since the elements are T rather that Student.
What should I do?
It is not possible to make 'conditional' methods, as in, it is not possible to make a method which only exists for some of the types. A Foo<T> object doesn't change what methods it has based on the T.
You can create a subtype:
public class Foo<T> {
private List<T> elems = ...;
void bar();
}
public class StudentFoo extends Foo<Student> {
void baz() {
for (Student s : elems) {}
}
}
works fine. But that isn't going to magically give all Foo<Student> objects a baz method; you'd have to make them specifically as new StudentFoo(), not as new Foo<Student>().
Suppose we have the following:
public class Parent{
public void setXXX(String XXX);
public String getXXX();
}
public class Children extends Parent{
....
}
Now I want to create a method called clone List like the following:
public List<Something> cloneList(List<Something> original){
List<Something> newList=new ArrayList<>();
for(Something e:original){
Something newE=new Something();
newE.setXXX(e.getXXX());
newList.add(newE);
}
return newList;
}
The thing is we want cloneList can be applied to both List<Parent> and List<Children>, so is there anyway that applicable for "Something"?
Something cannot be "? extends Parent" or "Parent" due to the Java Collection<Parent> incompatible with Collection<Children>
Assumption:
1. Don't want to use any serialization approach or reflection.
We are unable to modify the Parent and Children class. This is predefined in 3rd party Jar.
SuperParent class is not possible because we cannot modify Parent as stated in 2.
That is not possible in Java. Take a look at Generic syntax for extends or equal to.
You could change your method as follows and make your Parent class extend SuperParent.
public static <T extends SuperParent> List<T> cloneList(List<T> original, Class<T> type) throws IllegalAccessException, InstantiationException {
List<T> newList=new ArrayList<>();
for(T e : original){
T x = type.newInstance();
x.setXXX(e.getXXX());
newList.add(x);
}
return newList;
}
Also, you could choose another cloning approach. For example, using Apache Commons' SerializationUtils:
List<Children> result = (List<Children>) SerializationUtils.clone(originalList);
You cannot use generics this way, only reflection.
For a type variable T, you cannot use new T(). That's because generics are a compile-time mechanism, and new is used in run-time to create a specific-type object, and the compiler cannot create the appropriate reference to the type at compile time. So while this:
new ArrayList<T>();
is legal, because the compiler actually compiles it into the code for creating the raw ArrayList type, this:
new T();
is not, because the compiler does not even know what the actual class will be (even if it was just defined as T extends Parents it could be a class that has not even been written when the program compiled, like Grandchildren or something), and does not even know if it has a parameterless constructor.
In a general sense, you should be able to use a method having this signature:
public <T extends Parent> List<T> cloneList(List<T> original)
That's not your biggest problem, however. THAT would be obtaining copies of the list elements. Your code cannot use
T newE = new T(); // doesn't work
because the existence of a nullary constructor for type argument T cannot be guaranteed. Instead, you need a method that will return a correctly-typed copy. You cannot do this with complete type safety, but you can come close. You can implement these methods:
public Parent Parent.copy();
public Children Children.copy();
... in whatever way is appropriate, and then write your method like so:
public <T extends Parent> List<T> cloneList(List<T> original) {
List<T> newList = new ArrayList<>();
for (T originalItem : original) {
newList.add(original.getClass().cast(original.copy()));
}
return newList;
}
(Note that although the documented return type of Object.getClass() is Class<?>, which would not work for this purpose, the method documentation says that the return type is actually a bit more specific than that, enough so to make this work.)
Change the signature of cloneList to:
public <X extends Parent> List<X> cloneList(final List<X> original)
Then it will work, at least for the method signature. You can internally construct a List<Parent> and then cast it to List<X> and ignore the warnings if you need to; there's no way to find out the runtime type of "X".
I want to generate a generic list where the type of the list is known only at runtime (its the type of the object, which create that list).
Complete description:
I want to implement this functionality in a abstract class, so i know the parent class before runtime.
Don't know how to do that.
Class myClass = getClass().getSuperclass();
LinkedList<myClass> list = new LinkedList<myClass>();
does not work. Any ideas?
or you can just write:
List<Object> list = new LinkedList<Object>();
Generics are largely a compile time feature so it doesn't have any meaning in this context.
You can just write
List list = new LinkedList();
I usually prefer ArrayList if you can use that. ;)
Simply use Non-Generics ArrayList
ArrayList arrList = new ArrayList();
even you can use the <?>
Thought using the List will be good, as it show the principle of "Program in Interface rather than implementation"
Another option would be to parametrize the abstract class with the type of the extending class. This is a bit over-engineered, but should work:
package test;
import java.util.ArrayList;
import java.util.List;
public class AbstractListHolder<T> {
private List<T> list = new ArrayList<T>();
List<T> getList() {
return list;
}
}
class ListHolder extends AbstractListHolder<ListHolder> {
void doSomething() {
getList().add(this);
}
}
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.
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) {...}
}