In java one can cast instance of a class B into an instance of class A provided that B extends A.
Can this be done also for type-parametrized classes? For instance, for B extending A, I'd like to do the following:
List<A> l = new ArrayList<B>();
I think this should be legal, but the compiler doesn't agree with me on this point, so I tricked it with the following hack:
List<A> l = (List<A>)(List) new ArrayList<B>();
…but I think that I'll be hit by a velociraptor. Is there an elegant way of doing this?
I think this should be legal, but the compiler doesn't agree with me on this point
Generally, when humans and compilers disagree, it is safe to take the compiler's side. Instead of tricking the compiler, you should first understand why it does not allow the conversion.
Consider this: you make a List<B>, like this:
List<B> listB = new ArrayList<B>();
Everyone is happy so far. Now let's do what you think is correct:
// This does not compile, but let's pretend that it does
List<A> listA = (List<A>)listB;
At this point, we have listA that is entirely ours! Let's play with it: let's say we have another class C extending A. Can we do this?
listA.add(new C()); // Why not? The compiler should allow it!
But wait, do you see what we have done? Our listB contains a C now! This is why the compiler does not allow the conversion; this is also why your code is not safe in cases when you attempt to add anything to the list.
To work around this issue, you can use wildcards: List<? extends A>. However, this will limit your lists to read-only access. If you must allow writing (and you know that you are going to set only the right stuff) use the non-generic List instead.
List<? extends A> l = new ArrayList<B>();
You should read about generics in java here
Two ways to do it.......
- This way of handling the Collections are done, cause Array is checked during compile and as well as runtime, but Collections are checked only during Compile time.
- So there should NOT be an accidental addition of another type of object in to another type of Collection, ie Dog Object into a Cat Collection.
1.
public <T extends A> void go(ArrayList<T> aList){
}
2.
public void go(ArrayList<? extends A> aList){
}
In a non generic context, if B "is a" A, then B can be substituted for A, but A cannot be substituted for B.
With generics, List and List are both "is a" Collection<E>; that is, List is not a subclass of List, and therefore, you cannot substitute List for List.
The specific content on generics you're looking for can be found here, copied below for your convenience.
Given two concrete types A and B (for example, Number and Integer), MyClass has no relationship to MyClass, regardless of whether or not A and B are related. The common parent of MyClass and MyClass is Object.
Related
Maybe it is silly question: Let say I have
abstract class A<T> {
List<Wrapper<T>> doStuff()
}
And I have class B extends A<String> and class C extends A<Integer>
Now I want to have:
List<A> aces = list with instances of B and C;
List<Wrapper> wrapperedItems = flattened list of lists returned from doStuff() on all items in aces
At this point I don't care what type is within Wrapper.
And my question is: shall I use somewhere <?> or can I skip it? What is the difference?
If you use List<Wrapper<?>>, you will be able to get objects out of the list, but you will not be able to add new items. The wildcard essentially correcponds to a type that is different from any other type, including other wildcards.
If you use List<Wrapper>, you will be able to both get list items and add new ones, but the onus is now up to you to guard for improper type casts.
Consider the following code:
MyClass myClass= new MyExtendedClass();
myClass.method();
where MyExtendedClass be a subtype of MyClass. As i understood on a compile state compiler on the second string compiler checked of existence method method()in a source code of MyClass. Is it correct reasoning? Now consider
List<Integer> ints= new ArrayList<Integer>();
ints.add(2);
List<? extends Integer> lst = ints;
lst.get(0);
Where i can see the sources of List<? extends Integer>?
Now consider:
new ArrayList<Integer>().getClass().equals(
new ArrayList<String>().getClass());// return true.
So
In a run time classes ArrayList<Integer> and ArrayList<String> are equals, but on a compile state it is not true. Why? Where are the sources of ArrayList<String> and ArrayList<Integer>?
Search for type erasure. To start you can refer http://docs.oracle.com/javase/tutorial/java/generics/erasure.html
For myClass.method(); to be legal, the MyClass class or interface must declare the method method. That is correct.
The reason that the getClass results are equal for ArrayList<Integer> and ArrayList<String> is that at runtime, type erasure erases the generic type parameters, leaving ArrayList in both cases, and of course they are both equal. It is only the compiler that can distinguish between ArrayList<Integer> and ArrayList<String>. The JVM never knew about generics, so type erasure occurs to be backwards compatible with pre-generics (pre-Java 1.5) applications.
Source code for List
Source code for ArrayList
Java does not work the way that C++ does. In C++ a "template" causes a complete generation of a new class with that referenced type in it. Java, however, does not generate a new class for every templated instantiation. Instead, the type information is carried as a setting at compile and run time. So ArrayList<Integer> and ArrayList<String> make use of the same actual class.
The type checking is in the compiler, and for simple classes this is easy.
class Gem {...}
class Ruby extends Gem {...}
class Diamond extends Gem {...}
Gem a;
Ruby b;
Diamond c;
a = b; //no problem allowed
b = a; //type problem, need to cast it!
b = c; //never allowed!
But parameterized classes are much more complicated
List<Gem> la;
List<Ruby> lb;
List<Diamond> lc;
la = lb; //not allowed because type is different even though Ruby extends Gem
List<? extends Gem> ld;
ld = la; //allowed
ld = lb; //also allowed
ld = lc; //also allowed
The wildcards are needed to allow you to have a collection variable that holds more than one kind of collection, where the differences are in the type parameter. In all cases the object class (and source code) remains the same.
I learned that if I have two classes:
public class A {}
public class B extends A {}
I can cast from a List<A> to List<B> by doing this:
List<B> list = (List<B>)(List<?>) collectionOfListA;
This would generate a warning, but it works. However, I read that this is not recommended. Is it true in that case?
If I know it's returning me a List<B>, why making this cast is such a bad practice?
Please note that I want to know why, in this case, this is a bad practice, not why there is a warning. And "it's a bad practice because it generates a warning" is not a good answer.
An apple is a fruit, but a list of apples is-not a list of fruit.
If it was, you could put a banana in a list of apples
Casting the list directly is bad because it subverts the Java type system. The cast will always succeed, but the program may fail later with a ClassCastException when you go to retrieve items, at which point the source of the error may be unclear.
In general you want your program to fail as close to the source of the error as possible. If you cast the list, it may be passed around for a while before someone actually tries to access elements and by the time it throws a ClassCastException it might be really hard to track it back to that cast (at the very least hard-er than if the program failed at the cast).
From your comments it seems like you're sure that everything in listA is actually a B in which case I would recommend:
List<B> listB = new ArrayList(listA.size());
for (A a : listA) {
if (a == null || a instanceof B) {
listB.add((B) a);
} else {
//Either ignore or throw exception
}
}
For this to make sense, you have to understand that List<A> and List<B> are two different types, not hierarchically related, even if A and B are hierarchically related. Even if B was a subclass of A, casting a List<B> to a List<A> is bad because it could allow you to add to that list instances of A (but not B) and the compiler would happily do that, even though it wouldn't agree with the actual (runtime) type of the collection.
I didn't know that you can circumvent this in Java, but if you can it's only because of type erasure: because Java creates one class such as List<Object> and, for different "implementations" of that generic class, it merely adds casts in your code before compiling it. This is contrast to (say) C#, which actually uses the List<T> only as a "compiled" template (called "generic definition"), to create the concrete types on demand.
Generics work bit differently, so list< B > is not of type list< A > though B is a type of A.
Tiger class is extends from Animal Class.
When I declare: List<Animal> tiger = new ArrayList<Tiger>();. I will error at compile-time.
But, I think this line is true for polymorphism. Who can explain for me, please.
you cannot do
List<Animal> tiger = new ArrayList<Tiger>();
that in java. Generic type on left have to be exacly equal (or may not have to be equal, if wild cards are in game - ? extends T or ? super T) to generic type on right.
If it was possible then it would be impossible to add new Lion to list declared as list of Animals - that would make no sense.
What you can do is:
List<Animal> tigers = new ArrayList<Animal>();
tigers.add(new Tiger());
(all family of Animals, including Tigers)
or:
List<? extends Animal> tigers = new ArrayList<Tiger>();
tigers.add(new Tiger()); // Adding is immpossible now - list can be read only now!
(only subclasses of Animal) - list can be read only now!
A List<Animal> would allow you to add a cute little puppy. Which the tigers in the ArrayList<Tiger> would then eat.
Polymorphically speaking, you would have
List<Tiger> tigers = new ArrayList<Tiger>();
Which would allow you to replace use any implementation of List<Tiger> if you so desired, relying upon and using the functionality as defined by the interface. What you are trying to do isn't polymorhpism, it is simply an unsafe conversion (particularly for the aforementioned puppy) and is not going to work for reasons illustrated above.
The reasons for this are based on how Java implements generics. The best way I have found to explain it is by using arrays first.
An Arrays Example
With arrays you can do this:
Integer[] myInts = {1,2,3,4};
Number[] myNumber = myInts;
But, what would happen if you try to do this?
Number[0] = 3.14; //attempt of heap pollution
This last line would compile just fine, but if you run this code, you could get an ArrayStoreException.
This means that you can fool the compiler, but you cannot fool the runtime type system. And this is so because arrays are what we call reifiable types. This means that at runtime Java knows that this array was actually instantiated as an array of integers which simply happens to be accessed through a reference of type Number[].
So, as you can see, one thing is the real type of the object, an another thing is the type of the reference that you use to access it, right?
The Problem with Java Generics
Now, the problem with Java generic types is that the type information is discarded by the compiler and it is not available at run time. This process is called type erasure. There are good reason for implementing generics like this in Java, but that's a long story, and it has to do with binary compatibility with pre-existing code.
But the important point here is that since, at runtime there is no type information, there is no way to ensure that we are no committing heap pollution.
For instance,
List<Integer> myInts = new ArrayList<Integer>();
myInts.add(1);
myInts.add(2);
List<Number> myNums = myInts;
myNums.add(3.14); //heap polution
If the Java compiler does not stop you from doing this at compile time, the runtime type system cannot stop you either, because there is no way, at runtime, to determine that this list was supposed to be a list of integers only. The Java runtime would let you put whatever you want into this list, when it should only contain integers, because when it was created, it was declared as a list of integers.
As such, the designers of Java made sure that you cannot fool the compiler. If you cannot fool the compiler (as we can do with arrays) you cannot fool the runtime type system either.
As such, we say that generic types are non-reifiable.
Evidently, this would hamper pollymorphism as well pointed out. The solution is to learn to use two powerful features of Java generics known as covariance and contravariance.
Covariance
With covariance you can read items from a structure, but you cannot write anything into it. All these are valid declarations.
List<? extends Number> myNums = new ArrayList<Integer>();
List<? extends Number> myNums = new ArrayList<Float>()
List<? extends Number> myNums = new ArrayList<Double>()
And you can read from myNums:
Number n = myNums.get(0);
Because you can be sure that whatever the actual list contains, it can be upcasted to a Number (after all anything that extends Number is a Number, right?)
However, you are not allowed to put anything into a covariant structure.
myNumst.add(45L);
This would not be allowed, because Java cannot guarantee what is the actual type of the real object. It can be anything that extends Number, but the compiler cannot be sure. So you can read, but not write.
Contravariance
With contravariance you can do the opposite. You can put things into a generic structure, but you cannot read out from it.
List<Object> myObjs = new List<Object();
myObjs.add("Luke");
myObjs.add("Obi-wan");
List<? super Number> myNums = myObjs;
myNums.add(10);
myNums.add(3.14);
In this case, the actual nature of the object is a List of Objects, and through contravariance, you can put Numbers into it, basically because numbers have Object as the common ancestor. As such, all Numbers are objects, and therefore this is valid.
However, you cannot safely read anything from this contravariant structure assuming that you will get a number.
Number myNum = myNums.get(0); //compiler-error
As you can see, if the compiler allowed you to write this line, you would get a ClassCastException at runtime.
Get/Put Principle
As such, use covariance when you only intend to take generic values out of a structure, use contravariance when you only intend to put generic values into a structure and use the exact generic type when you intend to do both.
The best example I have is the following that copies any kind of numbers from one list into another list.
public static void copy(List<? extends Number> source, List<? super Number> destiny) {
for(Number number : source) {
destiny.add(number);
}
}
Thanks to the powers of covariance and contravariance this works for a case like this:
List<Integer> myInts = asList(1,2,3,4);
List<Integer> myDoubles = asList(3.14, 6.28);
List<Object> myObjs = new ArrayList<Object>();
copy(myInts, myObjs);
copy(myDoubles, myObjs);
I agree it's confusing. Here's what could go wrong if that type of statement were allowed:
List<Tiger> tigers = new ArrayList<Tiger>(); // This is allowed.
List<Animal> animals = tigers; // This isn't allowed.
tigers.add(new Lion()); // This puts a Lion in tigers!
Oh. Yes, you true. your code is right in Polymorphism thinking. But, look at my code that I use for a long time when I just a novie.
And you will see why you should thank to Collection:
class Animal{
}
class Tiger extends Animal{
}
public class Test {
public static void main (String[] args){
List<Animal> animal = new ArrayList<Animal>(); //obvious
List<Tiger> tiger = new ArrayList<Tiger>(); //obvious
List<Animal> tigerList = new ArrayList<Tiger>(); //error at COMPILE-TIME
Animal[] tigerArray = new Tiger[2]; //like above but no error but....
Animal tmpAnimal = new Animal();
/*
* will meet RUN-TIME error at below line when use Array
* but Collections can prevent this before at COMPILE-TIME
*/
tigerArray[0] = tmpAnimal; //Oh NOOOO. RUN-TIME EXCEPTION
/*
* Below examples WRONG for both Collection and Array
* Because here is Polymorphism problem. I just want to make more clearer
*/
List<Tiger> animalList = new ArrayList<Animal>();
Tiger[] animalArray = new Animal[2];
}
}
As you see my above code, Collections is so "intelligent" when prevent you use List<Animal> tigerList = new ArrayList<Tiger>();
You should imagine if someone use: tigerList.add(a Lion, a Cat,......); --->ERROR.
So, to Sumarize, here is the different:
ARRAY: check at RUN-TIME. You will feel more comfortable but DANGEROUS
COLLECTIONS: check at COMPILE-TIME. You will feel angry because it
notice error. But, you will prevent errors when Running. !!!!
Maybe below post is over of your question. But I suggest you use WildCard like:
List<? extends Animal> tigerList = new ArrayList<Tiger>();
Yes. You might see the idea behind this line. But, the MOST INTERESTING THING is: it will prevent you change the List. in this case, add method.
For example:
tigerList.add(TIGER); ERROR
yes. It will prevent you add a tiger, too :)
I have a class A and a class B extends A
In another class C I have a field
private List<B> listB;
Now, for some unusual reason, I have to implement this method in C
public List<A> getList();
I tried to do so by forcing an upcast of listB field to List<A> via a List<?> cast:
public List<A> getList(){
return (List<A>)(List<?>)listB;
}
Clients should do
List<A> list = getList();
for(A a:list){
//do something with a
}
I did some test and it seems work correctly, but honestly I am not sure of the all possible implications.
Is this solution correct? And Is it the best solution?
Thanks for your answers.
No, this isn't generally type-safe. The client shouldn't be able to do
List<A> list = getList();
because otherwise they could write
list.add(new C()); // Where C extends A
Then the original code which knows about the list as a List<B> will have problems when it tries to use it, assuming that every element is compatible with B.
You could either wrap the original list to make it read-only, effectively - or make getList return a List<? extends A>, which means that clients won't be able to add items to it anyway.
If the list implementation you're using is unmodifiable, then it won't actually cause problems - but I'd still personally avoid it where possible.
The problem with this is that clients can, unwittingly, insert A objects in what is actually a list of more specific B objects only:
c.getList().add(new A());
This will cause all kinds of breakage when your code tries to take an object from the list assuming that it's a B, but it isn't.
If your only goal is to let the client iterate over the list, it is better to hand out an Iterable<A> instead:
public Iterable<A> getAs() { return this.theListOfAs; }
Through this Iterable, one can only inspect and remove elements, but not add them.
If you want to disable removal as well, wrap the List's Iterable in your own implementation, throwing UnsupportedOperationException when remove() is called.