I'm having trouble casting a List of Fruit down to the Fruit subclass contained in the List.
public class Response {
private List<Fruit> mFruitList;
public List<Fruit> getFruitList() {
return mFruitList;
}
}
public class Fruit {
}
public class Orange extends Fruit {
}
List<Fruit> oranges = response.getFruitList();
How do I cast oranges so that it is a List of class Orange? Is this a bad design pattern? Basically I am getting a JSON Response from a server that is a List of Fruit. For each specific call to the Web Service, I know what subclass of Fruit I will get and so I need to cast that List appropriately.
If you known for each specific call that what subclass of Fruit you will get then you should use generics instead of casting lists.
public class Response<T extends Fruit> {
private List<T> mFruitList;
public List<T> getFruitList() {
return mFruitList;
}
}
Response<Orange> response = // create
List<Orange> oranges = response.getFruitList();
EDIT: By templates I meant generic types. Sorry, I had too much C++ nowadays
The whole idea behind typecasts is to be able to tell the compiler, "Hey, I know more about this than you do." In your code, the compiler cannot safely downcast the List<Fruit> to List<Orange> because it can't know what the list will contain at runtime.
If you're absolutely certain that the list will be only Orange instances, and it makes your code more manageable to downcast, go for it.
List<Orange> oranges = (List<Orange>) response.getFruitList();
The compiler will give you a warning, of course, since you're doing something it thinks you shouldn't do. And just know that the JVM may have the last laugh by throwing a CastClassException if you were wrong!
Think of generics like a gate for what types of objects a list can contain. Because of this inheritance and casting won't work in the way you would expect. In the example you gave you could put both Oranges and Apples in your List<Fruit>. If the list has both apples and oranges how can you cast it to a List<Orange>.
If you need a List<Orange> then why even bother with the List<Fruit>. If you are explicitly casting it anyway and you know exactly what it contains its probably a needless abstraction.
If you are working with an API you can't change but you know exactly what it contains then you should loop through with an instanceof check just to make sure and explicity cast each Fruit instance to Orange when you need to the Orange API.
You should cast List, and test if each elements are instanceof Orange, and after the test cast in Oranges. This is the "best pratice".
Given
List<Fruit> getFruits() {...}
You can't typecast
List<Orange> oranges = (List<Orange>) getFruits();
Due to type erasure, at runtime the type of getFruits is just List. The compiler will not even let you do the downcast (I was in doubt, so I tried in Eclipse before answering).
You could tell the compiler that your list will contain some subclass of Fruit, in that case, you need to use a wildcard on you method:
List<? extends Fruit> getFruits() {...}
Then the cast becomes possible, but with a type safety warning:
#SuppressWarnings("unchecked")
List<Orange> oranges = (List<Orange>) getFruits();
Given that the runtime type of getFruits is List, you can just discard the generics type information and use an unsafe assigment:
#SuppressWarnings("unchecked")
List<Orange> oranges = (List) getFruits();
Maybe a more elegant way as it clearly states your intention, although requiring more system resources would be:
List<Orange> oranges = Arrays.asList((Orange[])getFruits().toArray())
Arrays in Java preserve their type information at runtime, so the cast is valid and "safe" from the compiler perspective, but it can throw a runtime exception if you pass some apples in the fruit basket.
Related
I have a method that is supposed to analyze a String and determine the correct Class<T>, where T extends BaseClass.
Suppose that BaseClass is extended by Child1, Child2 and Child3.
In my method, I want to do something like this:
public <T extends BaseClass> Class<T> from(String number) {
if (number.contains("Child1")) {
return Child1.class;
} else if (number.contains("Child2")) {
return Child2.class;
} else if (number.contains("Child3")) {
return Child3.class;
}
throw new UnsupportedOperationException("Cannot recognize class from " + number);
}
The problem is that the above method doesn't compile on all return statements, because Required type is Class<T>, provided is Class<Child1> (same for Child2 and Child3 of course).
Hence, I am forced to (unchecked) cast:
return (Class<T>) Child1.class;
... which of course works fine, since Child1.class is compatible with Class<T>.
How can I do what I'm trying to do in a cleaner way (without having the warning)?
... which of course works fine, since Child1.class is compatible with Class<T>.
That's incorrect. Understandable, as you're making a common mistake.
The fix
The fix is to have the return type be Class<? extends T> instead. But, scratch that...
But this code is bad style
Generics serve to link things. Any type variable should therefore be declared once and used in at least 2 places, or it is one of:
useless.
actively misleading.
a type-safety breaking hack.
I assume you intended none of that. You have declared T once, and are using it once, which therefore means it's incorrect. The correct version is:
public Class<? extends BaseClass> from(String number) { ... }
This does everything you want. Specifically, something like this:
BaseClass bc = from("Child3").getConstructor().newInstance();
will just compile, no need for casts (you will need to festoon this up with rather a lot of try/catch, but no need for a cast, at least), and the compiler will not emit type safety warnings either. Which, presumably, is what you're trying to accomplish.
Explanation
In java, typing relationships are covariant. That means any type can stand in for any of its supertypes.
In other words:
Number n = Integer.valueOf(5);
is valid java.
But the thing that generics are for simply doesn't adhere to this rule. Here is a trivial example. Imagine generics was just as covariant as basic type usage in java would be. Then I could write this, it would compile, and that would be bad, because this code would then be breaking the typing system:
List<Integer> listOfInts = List.of(1, 2, 3);
List<Number> listOfNums = listOfInts;
listOfNums.add(Double.valueOf(5.5));
int value = listOfInts.get(3);
Go through the above code and you realize there's a fundamental issue here.
The fix is that generics are invariant - a type is a valid standin only for itself; not for anything else.
In other words, the one and only thing you can assign to a List<Number> is an expression of type List<Number> or perhaps ArrayList<Number> ( because the non-generics part is covariant, we're talking only about the stuff in the <>), not List<Integer>.
That's the fix - that's why the above code isn't actually a problem for the typing system in java - it simply won't compile.
Now, when there is no such thing as 'adding', this becomes dubious, and Class is just such a type: Yes it's got a type param but you can't 'break stuff' if generics was covariant. Unfortunately, the generics feature of java does not ship with a gigantic list of 'the generics on THIS type can be covariant, but here they cannot be'.
Instead, java lets you choose your variance, and the APIs change to reflect what that means:
List<? extends Number> list = someListOfIntegers; // co-variance
List<? super Number> list = someListOfObject; // contra-variance
List list = someListOfAnything; // legacy-variance a.k.a. raw
List<Number> list = someListOfNumber; // invariance
Of course, you don't get this stuff for free: That covariant list (List<? extends Number>), you cannot call add on this list, at all. Well, the literal .add(null), because null is a standin for all types, that works, but nothing else will, add is always a compile time error. That's the cost. If you opt out of all add methods on a list, then and only then can you write a method that accepts as parameter a list of numbers, or integers, or doubles, etc.
With Class it gets dubious (as there's no 'writing'), but the co/contra/invariant system is baked into generics and doesn't care about the fact that Class doesn't have any add-style methods in it.
I have a very basic question.
The code below doesn't compile (assume Apple Extends Fruit):
List<? extends Fruit> numbers = new ArrayList<>();
numbers.add(new Apple()); //compile time error
When reading about why not, I understand the words but not the concept :).
Let's assume first Fruit is NOT an abstract class. I understand that that since we're dealing with multiple subtypes all of which extend Fruit. Supposedly since we can't tell the exact type of fruit, we can't put anything in the collection. There's a couple things I don't understand:
1) Apparently we cannot know which fruit it is which confused me. Wouldn't we be able to tell the specific type through a typeof or other instanceof check while iterating through the collection?
2) Assuming Fruit is a concrete class, why wouldn't we be allowed to add instances of Fruit? It seems like that would make sense because you would know at minimum the API for Fruit. Even if you don't know the exact subtype of Fruit, at least you can invoke the standard methods on Fruit().
I feel like this should be rather obvious but something isn't clicking for me. Any help is appreciate. Thanks!
The best way to understand this is to think of the wildcard as saying something about the list, not the fruit. In other words:
List<Banana> allBananas = getMyBananas();
enumerateMyFruit(allBananas);
static void enumerateMyFruit(List<? extends Fruit> myFruit) {
for (Fruit fruit : myFruit)
System.out.println(fruit);
}
When we pass allBananas to enumerateMyFruit, inside the method we lose information about the original declared type of the list. In this example we can very clearly see why we shouldn't be able to e.g. put apples in a List<? extends Fruit>, because we know that the list is actually a List<Banana>. Again, the wildcard is telling us something about the declared type of the list.
List<? extends Fruit> should be read as something like "a list originally declared to hold Fruit or some subtype of Fruit, but we don't know what that declared type is anymore". All that we know is that everything we pull out of the list is a Fruit.
Also, you are right, we could iterate the list and use instanceof to find out what is really in the list, but this wouldn't tell us the original declared type of the list. In the above code snippet we would find out that everything in the list turned out to be a Banana, but I could have just as easily declared allBananas as a List<Fruit>.
You might also see why a List<Dog> is not a List<Animal> which explains some of this. The wildcard is how we have covariance among generic types. a List<Dog> is not a List<Animal> but it is a List<? extends Animal>. This comes with the restriction that we can't add to a List<? extends Animal>, because it might be a List<Dog>, a List<Cat> or something else. We don't know anymore.
There's also the ? super, which is the opposite. We can store Fruit in a List<? super Fruit> but we don't know what kinds of objects we will pull out of it. Its original declared type might actually be e.g. a List<Object>, with all kinds of other stuff in it.
First remember that for generic parameters without wildcards, you can't substitute one for another. If a method takes a List<Fruit> it won't take a List<Apple>, it has to be an exact match. Also remember this is about the static type of the variable, there's no direct connection to the contents. Even if your List<Fruit> contains all Apples, you still can't substitute it for a List<Apple>.
So we're talking about type declarations, not about what's in the collections.
Also remember instanceof is done at runtime, generics work at compile time. Generics are about helping the compiler figure out what types things are so you don't have to resort to instanceof and casting.
When a method foo takes a parameter with the generic type List<? extends Fruit>, that is a way of saying that the method can take a range of types, in this situation those being any of the following:
You can pass in a List<Fruit>
You can pass in a List<Banana>
You can pass in a List<Apple>
(etc., for whatever subtypes of Fruit you have)
So your method can work with a list of any of these, however, the method body has to be valid for any of them. When you add an Apple to the list, that works for the case where what's passed in is a List<Apple>, it works for List<Fruit>, for List<Banana> not so much. (And making Fruit concrete doesn't help matters,
adding a Fruit doesn't work for the List<Apple> case either.)
That is why there's a rule that anytime the wildcard type extends something, adding stuff is not possible, there's no way it can work for all the possible types that can be passed in.
I've found out that if you have a generic class with an array that depends of type parameter of that class, you can't initialize that array in a usual way:
class Foo<T> {
private T[] a;
Foo() {
a=new T[5]; //doesn't compile
}
}
You can only do something like this:
class Foo<T> {
private T[] a;
Foo(T[] a) {
this.a=a;
}
}
But this code is possible:
ArrayList<Integer> list=new ArrayList<>();
Why? How does the ArrayList overcome that thing? I've looked into its code but couldn't figure it out. It seems like it just stores things in Object[] array which seems wrong.
The array inside ArrayList is always of type Object[], because you can store anything inside such an array. The type argument enables type safety none the less on a method level and this is what matters.
Edit to address the topic of type erasure as requested:
Type erasure in Java means that generic type arguments are only present during compile time, where it matters most to ensure type safety. Generics have been implemented this way to keep newer versions of Java compatible to older ones.
The drawback of this is that one can never instantiate a generic type, because it is never known at runtime.
Consider this as an analogy:
You own a pencil company. You drive around with pencils in your van.
You could put flowers in your van: it's a van, after all, so you could put anything you want (that fits) in there. Baseballs, gorillas, helium balloons.
But you choose not to: you're a pencil company, so all you put into your van is pencils. If your friend from the burger company asks you to put some burger patties in your van, you will say no: this van is only for pencils.
So, when you come to take something out of your van, you know it's going to be a pencil, because you made sure the only thing that went in through those doors was a pencil.
And so it is with an ArrayList<T>: you could store anything in that Object[], but you don't: you can only store Ts in there, because you can only add things to it through the add(T) or addAll(Collection<? extends T>) methods.
So it doesn't matter that your elements are being stored in an Object[], rather than more-specifically typed array: the only things that you'll get out of an ArrayList<T> - via its get() (etc) methods - are instances of T, or one of its subtypes.
I'm trying to design my own programming language, and am thinking about generics. I've been doing Java for quite a while now and know about the extends and super generic bounds.
I'm reading this post and trying to understand the need for the lower bounds.
In my language, I am planning to do generics the same way as a regular field, if you say List<MyObject>, you can store either a MyObject, or any subtype of MyObject. Makes sense right?
On the post, they have the following class hierarchy:
class Person implements Comparable<Person> {
...
}
class Student extends Person {
...
}
They then have a sort method:
public static <T extends Comparable<T>> void sort(List<T> list) {
...
}
What I think, is that you should be able to send a List<Student> to this method. As a Student extends Person, the compare method would be handled by it's superclass, Person.
The reason for the error message is that the compiler infers the type parameter of the sort method as T:=Student and that class Student is not Comparable<Student> . It is Comparable<Person> , but that does not meet the requirements imposed by the bound of the type parameter of method sort. It is required that T (i.e. Student ) is Comparable<T> (i.e. Comparable<Student> ), which in fact it is not.
The above doesn't make any sense to me...you should be able to do student.compare(person), so why doesn't this work?
Maybe it's saying that Student should implement it's own comparable method so that Student has a say in the comparison? You don't need to do anything special, just override Person's method. You won't be able to guarantee you are comparing to another Student, but that can be checked with instanceof.
Is there something I'm missing here?
And after all this thinking, I'm now wondering what the purpose of extends is. From my understanding, in a List<MyType>, you can only put a MyType in, not any of it's subclasses. As mentioned above, this doesn't make any sense to me and you should be able to put any subclass in the list like a field.
I should probably make this clear, it's not "why doesn't it work in Java", but "why doesn't it work in generics theory". I just tagged java because that is where I'm making my comparisons.
First: The method declaration
public static <T extends Comparable<T>> void sort(List<T> list)
does not make much sense for me. I thing it should be
public static <T extends Comparable<? super T>> void sort(List<T> list)
Then it would be possible to write sort(listOfStudents). Now I will explain the advantage of upper and lower bounded wildcards:
Polymorphism of type parameters is not transferred to it's generic type
This mean a list of students (List<Student>) is not a list of persons (List<Person>). A instruction like
List<Person> list = new List<Student>();
would fail in Java. There is a simple reason: list.add(new Person()); would be illegal for a list of students but not for a list of persons.
Upper Bounded Wildcards
But maybe you have a function which doesn't care whether the objects are subclasses or not. For example: You could have a method like this:
void printAll(List<Person> list)
They just print some data about all persons to stdout. If you have a list of students (List<Student> listOfStudents) you could write:
List<Person> listOfPersons = new ArrayList<>();
for (final Student student : listOfStudents) {
listOfPersons.add(student);
}
printAll(listOfPersons);
But you may see that it isn't a very nice solution. Another solution would be to use upper bounded wildcards for printAll:
void printAll(List<? extends Person> list)
You can write something like Person person = list.get(0) in printAll. But you cannot write print.add(new Person()) because list could be a list of students or something else.
Lower Bounded Wildcards
Now the same in the other direction: Lets say you have a function which generates some students and put them in a list. Something like this:
void generateStudents(List<Student> list) {
for (int i = 0; i < 10; ++i) {
list.add(new Student());
}
}
Now you have a list of persons (List<Person> listOfPersons) and want to generate students in this list. You could write
List<Student> listOfStudents = new ArrayList<>();
generateStudents(listOfStudents);
for (Student student : listOfStudents) {
listOfPersons.add(student);
}
You may see again, that it is not a very nice solution. You could also change the declaration of generateStudents to
void generateStudents(List<? super Student> list)
Now, you can just write generateStudents(listOfPersons);.
I think your confusion may be coming from the fact that while elements of List<Student> can be compared to each other by virtue of the fact that class Student subclasses Person which implements Comparable<Person> (class Student therefore inherits compareTo(Person o), which can be called with an instance of Student), you still cannot call the sort method with a List<Student>...
The problem is that when the Java compiler encounters the statement:
sort(studentList);
Where studentList is an instance of parameterized type List<Student>, it uses type inference to infer that the type argument to the sort method T is Student, and Student does not satisfy the upper bound: Student extends Comparable<Student>. Therefore, the compiler will throw an error in this case, telling you that the inferred type does not conform to the constraints.
The article that you linked to shows you that the solution to this is to re-write the sort method as:
public static <T extends Comparable <? super T > > void sort(List<T> list)
This method signature loosens the constraint on the type parameter so that you can call the method with a List<Student>.
I'm not too clear on the last part of your post:
in a List<MyType>, you can only put a MyType in, not any of it's subclasses.
If you're referring to the elements of List<MyType>, yes, you can put any element that is a subtype of MyType in this list, say, MySubType. If you're referring to a variable with List<MyType> as its reference type, then no, you cannot put a reference to List<MySubType> in a variable of type List<MyType>. The reason for this is easy to see when you consider the following:
List<MyType> a;
List<MySubType> b = new ArrayList<>();
a = b; // compile-time-error, but assume OK for now
a.add(new MyType()); // Based on the type of a, this should be OK, but it's not because a is actually a reference to List<MySubType>.
I also think you should refer to Java Generics by Wadler and Naftalin, an excellent introduction to Java (5+) Type System.
When you ask "what is the purpose of extends keyword?" based on your observations about collections of objects, the first thing you should remember is generic collections are tricky. I quote from the Wadler/Naftalin book (emphasis mine):
In Java, one type is a subtype of another if they are related by an
extends or implements clause: Integer is a subtype of Number.
Subtyping is transitive.
If A is a subtype of B, B is the supertype of A.
Liskov’s Substitution Principle tells us that wherever a value of one
type is expected, one may provide a value of any subtype of that type:
a variable of a given type may be assigned a value of any subtype of
that type, and a method with a parameter of a given type may be
invoked with an argument of any subtype of that type.
It’s because of violation of Liskov’s Substitution Principle (that
would arise very rapidly in practice) that a List<Integer> is not a
subtype of List<Number> although Integer is a subtype of Number. The
other way round also does not work because List<Number> is NOT a
subtype of List<Integer>.
This paragraph should help us understand why the keyword extends is essential to support inheritance and polymorphism, and yet it (sort of) comes in the way of generic collections.
I think your question would be stated better as: "What are valid use cases for using lower bound generics in JAVA"? I had the same question and it makes sense to use the upper bounds when you have a list and you want to use different types that are all subtypes or the supertype of a hierarchy of classes.
For example, you want methods to populate a list of numbers with int, double, and long. Using extends you can do this as they are all subtypes of Number.
On the other hand, if you want to restrict methods to only use Integer, then you want the more narrowly defined class so that only int is allowed, not float, double, etc.
From the java docs:
The abstract class Number is the superclass of platform classes
representing numeric values that are convertible to the primitive
types byte, double, float, int, long, and short.
The Integer class wraps a value of the primitive type int in an
object. An object of type Integer contains a single field whose type
is int.
A better example might be work you are doing at the JVM level, so you want to restrict your methods to use the VirtualMachineError to get specific information written to the logs, rather than using the Error class, from which it inherits, to limit the number of errors you write to the logs for something very refined, possibly relating to some debugging task.
These are obviously contrived examples, but the theme is the lower bounds can be used to restrict method type parameters.
This is an Android app, but presumably it happens the same in Java. I have a type LevelFactory from which I derive Level1Factory, Level2Factory, etc. I want to have an array of these classes so I can instantiate a level given the array index.
I can just have a Class[] and put them in that and then just cast them to LevelFactory when I need to use them, but I was wondering what the proper thing to do is.
This is obviously an error "Incompatible types":
new Class<LevelFactory>[] {Level1Factory.class,Level2Factory.class};
However, I was surprised to see that this is also an error "Generic array creation":
new Class<? extends LevelFactory>[] {Level1Factory.class,Level2Factory.class};
The following works, but it gives the "Unchecked assignment" warning when assigned to a variable:
new Class[] {Level1Factory.class,Level2Factory.class};
The last is the only option I can get to work. I just ignore the warning, but I would like to do it using generics if that's actually possible.
I would recommend you to read Item 25 "Prefer lists to arrays" of book "Effective Java". There Joshua Bloch writes:
Why is it illegal to create a generic array? Because it isn’t typesafe. If it were
legal, casts generated by the compiler in an otherwise correct program could fail at
runtime with a ClassCastException. This would violate the fundamental guarantee provided by the generic type system.
UPD: Maybe with concrete example it would be more understandable.
First of all arrays are covariant which means that SuperClass[] can be cast to SubClass[] and vice versa. It also means that it's legal to cast AnyConcreteClass[] to, say, Object[].
So Let's assume that it's possible to have Set<Cat>[] (but it is NOT). If somebody cast this array to Object[] and then add there a set of Dog instances, Java couldn't guarantee anymore that our array contains only sets of Cat instances. Breaking type safety it breaks essence of generics. That is why it's illegal have generic array.
Set<Cat>[] cats = new Set<Cat>[]; // illegal
Object[] objects = cats;
objects[1] = new Set<Dog>();
cats[1].add(new Cat()); // Oops! TypeCastException
Honestly saying this example also was taken from Effective Java :)
Two questions:
Do you really need an Array? Arrays don't work great with generics. So an ArrayList<LevelFactory> might be the better solution
Do you really need the downcast to the special type (Level1Factory, Level2Factory)? If they have a common super method which is defined in LevelFactory (lets say Level getLevel()) you should not need to downcast them. Just call getLevel() and you get the correct Level instance from your factory.
Another note because this seems to be a common pitfall:
new Class<? extends LevelFactory>
This is not a valid statement (it does not matter if its an array or not). <? extends T> is only valid for the type on the left side. It defines that the generic type of the created Object can be T or derived from T. For Collections this does not mean that they can store objects of T or derived from T (which can a Collections of T anyway).
List<LevelFactory> list = new ArrayList<LevelFactory>() This means you can add objects of LevelFactory, Level1Factory and Level2Factory to list. When you want to receive objects from list they are of type LevelFactory.
List<? extends LevelFactory> list = new ArrayList<LevelFactory>() Means you can receive objects of LevelFactory from list. BUT you cannot add any object to list in a typesafe way because you don't know the exact generic type of list. That because you can also assign new ArrayList<Level1Factory>() to list. Which means that you can't even add LevelFactory objects to list because they don't implement Level1Factory.
In general <? extends Something> on collections is not what you want in most cases.
You can't create array this way new SomeClass<Type>[10] but only this way new SomeClass[10]. Consider using ArrayList<SomeClass<Type>> instead.