This is probably a java 101 question. But I've been away from java for ten years, so it's new to me.
I have 3 classes: Dog, Cat, Mouse
Each has its own ArrayList
e.g., ArrayList<Dog> dogs = new ArrayList<Dog>();
Dog Cat and Mouse implement AnimalInterface (which has methods such as getFurColor() and setFurColor(Color c)).
I have a method called, say, changeFurColor(ArrayList <AnimalInterface>list).
Moreover, changeFurColor() sorts the input ArrayList using a comparator that implements <AnimalInterface> so I need this parameterized type in my changeFurColor() method.
I call the method with changeFurColor(dogs);
However, this won't compile. The type <Dog> does not match the type <AnimalInterface> even though the former implements the latter.
I know I can simply use ? as the type for the changeFurColor argument and then cast or do instance of within the method as a check, but then I can't sort the list with my comparator (and it seems silly to have 3 different comparators).
I can't type all ArrayLists with <AnimalInterface> because I don't want to risk a dog in with the cats.
I am sure there is a simple solution, but none of my books provide it and I can't find it online
pseudoCode:
public interface AnimalInterface
{
public Color getColor();
......
}
public class Dog implements AnimalInterface
{
}
public class Cat implements AnimalInterface
{
}
public void example()
{
ArrayList<Dog> dogs = new ArrayList<Dog>();
ArrayList<Cat> cats = new ArrayList<Cat>();
ArrayList<Mouse> mice = new ArrayList<Mouse>();
changeFurColor(dogs)
}
public void changeFurColor(ArrayList <AnimalInterface> list)
{
... ..
Collections.sort(list, MyAnimalFurComparator);
}
public class MyAnimalFurComparator implements Comparator<AnimalInterface>
{
#Override
public int compare(AnimalInterface o1, AnimalInterface o2)
{
...
}
}
UPDATE
changeFurColor(dogs) does not compile
This is the correct answer (for neophytes that follow me)
Credit goes to Solitirios for his comment on the question.
public static <T extends AnimalInterface> void changeFurColor(ArrayList<T> list) –
I don't see any issues in the design.I'd need the exact code to tell what's exactly wrong, but The possible errors are:
You are not implementing the AnimalInterface method in the animal classes (Dog,Cat..), the method need's to be implemented explicitly:
class Dog implements AnimalInterface{public Color getColor(){return new Color();}}
class Cat implements AnimalInterface{public Color getColor(){return new Color();}}
You are passing a Comparator class, insteads of a instance in the Collections.sort method:
Collections.sort(list, MyAnimalFurComparator);
You need to change it to this:
Collections.sort(list,new MyAnimalFurComparator());
Also the compare method of your MyAnimalFurComparator should return an int
Related
I'm looking for a type-safe, checked solution to add an element to a list whose generic requires both a class and an interface. The example illustrates what I'd like to do - add an object whose type Cat extends Animal and implements Quadruped to a list List<T> where T extends Animal & Quadruped.
class Example {
public interface Quadruped { };
public static class Animal { };
public static class Cat extends Animal implements Quadruped { };
public <T extends Animal & Quadruped> List<T> getQuadrupedAnimals(){
List<T> result = new ArrayList<>();
Cat cat = new Cat();
result.add(cat); // compile error
return result;
}
}
Of course casting cat to B would resolve the compile error but that would be unchecked.
Is there any solution for this at all? If not, does anyone know the reason why the compiler does not allow this?
does anyone know the reason why the compiler does not allow this?
T is a specific Animal and Quadraped, chosen at the call site of getQuadrupedAnimals().
Legal invocations of that method include:
List<Dog> listOfDogs = getQuadrupedAnimals();
List<Hamster> listOfHamsters = getQuadrupedAnimals();
so you can't put a Cat into that list (nor a Dog nor a Hamster, for that matter), because whatever you put in there might not be castable to the type the caller wants. (You can put literal null in the list, though).
The solution is to declare the return type as List<Cat> (or List<? extends Cat>, or List<? super Cat>), if you want to return a list containing Cats.
An intersection type is only really useful if you use it in a parameter, e.g.
public <T extends Animal & Quadruped> List<T> getQuadrupedAnimals(T animal){
return List.of(animal);
}
or
public <T extends Animal & Quadruped> int countLegs(List<T> animals){
return 4 * animals.size();
}
Let's say I have an interface Animal with the method String getRace() and a subclass Dog. I keep some animals in a List<Animal>.
Now if I wanted to sort that list and passed a Function<Animal, String> into the method to use it together with Comparator.comparing(...) that would work fine. However, if I reference a function from Dog to pass as an argument, the compiler throws the following error:
The type Zoo.Dog does not define getRace(Zoo.Animal) that is applicable here.
even though it's a subclass of Animal and does define that function.
Here's the code:
import java.util.*;
import java.util.function.*;
import java.util.stream.*;
public class Zoo {
public interface Animal {
public String getRace();
}
public class Dog implements Animal {
public String getRace() {
return "Some race";
}
}
private List<Animal> animals = Arrays.asList(new Animal[] {});
public void sort(Function<Animal, String> function) {
animals = animals.stream().sorted(Comparator.comparing(function)).collect(Collectors.toList());
}
public static void main(String[] args) {
Zoo zoo = new Zoo();
// Error: "The type Zoo.Dog does not define getRace(Zoo.Animal) that is applicable here"
zoo.sort(Dog::getRace);
}
}
Why is that?
I've found this question, but in that example the classes are in no relation to each other.
This works:
public static void main (String[] args) {
Zoo zoo = new Zoo();
zoo.sort(Animal::getRace);
}
The reason is simple:
To sort a List of Animal using a Comperator, it needs to be a Comperator<Animal>. This means the function you pass into Comperator.comparing needs to
be of a Function<? super Animal,? extends Comperable>. This is not
true for Function<Dog, String>, however it is true for
Function<Animal, String> and Function<Object, String>.
You correctly define your .sort() to take a Function<Animal, String> - but you pass in a member function of Dog - this is essentialla a Function Dog -> String, or Function<Dog,String> in functional interfaces.
In less technical terms: yes, a Dogcan do everything an Animal can. The way around however is not true - a Dog can have a function that not all animals have. Therefore, it's not valid to use a Function<Dog,?> in place of a Function<Animal,?> - but it would be ok the way around.
Dog::getRace expects a Dog instance, but animals.stream() provides Animal instances.
What should work, I guess, since Dog implements Animal and getRace is declared in Animal:
zoo.sort(Animal::getRace);
Let's assume I have a super class called animal, and three sub classes respectively called Dog, Cat, and Lion that extend the animal class.
Then, I create an object each for the sub class and add them into an ArrayList<animal>.
When I am going through that list, how can I distinguish between a Dog or a Cat object as every object in the ArrayList is of type animal?
You should use instanceof operator:
if (animal instanceof Dog) {
//something
} else if (animal instanceof Cat) {
//something
} else if (animal instanceof Lion) {
//something
}
However, you should think twice if you really need to use it. In most cases instanceof is a bad OOP practise.
The point of inheritance is that you don't need to know what the type of animal each object, when you call method that have to animal object then each subClass can implement this method as you want.
If you anyway need to know what the type you can use the keyword instanceof, but if you need to use it probably you did something wrong as OOP programing
What Chief Two Pencils is trying to say in his comments is that you can use instanceof but in your situation there is a much better OOP design pattern to be used. For example, instead of distinguishing the object type, why don't you simply have a base method in Animal and override in each subtype? For example, apply. This way you can just call animal.apply() and the correct method for the type will apply.
Here's an example of what he means:
import java.util.ArrayList;
import java.util.List;
abstract class Animal {
abstract void apply();
}
class Dog extends Animal {
#Override
void apply() {
System.out.println("Bark bark!");
}
}
class Cat extends Animal {
#Override
void apply() {
System.out.println("Meeeeeow!");
}
}
class Lion extends Animal {
#Override
void apply() {
System.out.println("Rrrrrroar");
}
}
public class Main {
public static void main(final String[] args) {
final List<Animal> animals = new ArrayList<>();
animals.add(new Dog());
animals.add(new Cat());
animals.add(new Lion());
// Dog
animals.get(0).apply();
// Cat
animals.get(1).apply();
// Lion
animals.get(2).apply();
}
}
As you can see, I didn't need to check which type it for the correct behavior to be invoked for each animal. This is what polymorphism is all about.
In Java you would have to use instanceof. You will find people discourage the use of instance of. Most people consider it a code smell. Why? Because the whole reason to use a typing system is so that you can treat different types as if they were the same. So in other words, if you collect a bunch of different types, based on their having similarities embodied in your base class, you should be satisfied with accessing each through that base class reference. If you are writing code that is checking each type and then doing different things, that means your type system is leaky.
For instance, consider some operation you might want to perform. Maybe you have to feed each animal (in a rescue center). You would define an operation in the base class like 'likes to eat' that would indicate what that animal wants. Each subclass would provide its own implementation of that method. But, from your main code, you could just iterate through the collection and call the method from the base class reference, not knowing the details of each. In this scenario, your model has afforded extension. Remember the adage: open to extension, closed to modification (attributed to Bertrand Meyer).
If you just throw a bunch of related objects into a collection and decide 'well I'll figure out what to do with each given the circumstances I find,' then you are not following this principle.
Use instanceof to differentiate between different object types in the class hierarchy
import java.util.ArrayList;
import java.util.List;
public class Inheritance {
public static void main(String[] args) {
//create a list to hold all animals
List<Animal> animals = new ArrayList<Animal>();
//add animals
animals.add(new Animal());
animals.add(new Dog());
animals.add(new Cat());
for (Animal a:animals) {
//Use instanceof to determine type of object
if(a instanceof Dog){
Dog dog = (Dog)a;
dog.displayDog();
}else if (a instanceof Cat) {
Cat cat = (Cat)a;
cat.displayCat();
}else{
a.display();
}
}
}
}
class Animal{
public void display(){
System.out.println("This is a Animal");
}
}
class Dog extends Animal{
public void displayDog(){
System.out.println("This is a Dog");
}
}
class Cat extends Animal{
public void displayCat(){
System.out.println("This is a Cat");
}
}
I think I'm asking about covariant return types. I have some generated code that I'm trying to extend and use. Let's suppose I have the following two classes:
public class SuperParent
{
public List<SuperChild> getList()
{
return new ArrayList<SuperChild>();
}
}
public class SuperChild
{
}
Now, I want to derive new classes from these thusly:
public class SubParent extends SuperParent
{
public List<SubChild> getList()
{
return new ArrayList<SubChild>();
}
}
public class SubChild extends SuperChild
{
}
The problem is, apparently I can't override the getList() method because the return type doesn't match, despite both classes being extended in the same direction. Can someone explain?
Your understanding of co-variant is correct but usasge is not. List<SubChild> is not the same as List<SuperChild>
Consider this, List<Animals> is not the same as List<Dogs> and things can go horribly wrong if that was allowed. A Dog is an Animal but if it was allowed to assign like below:
List<Dogs> dogs = new ArrayList<Dogs>();
List<Animals> animals = dogs; //not allowed.
then what happens when you add a cat to it?
animals.add(new Cat());
and
Dog dog = dogs.get(0); //fail
So its not allowed.
As sugested by many others, use List<? extends SuperChild> as return type to solve your problem.
EDIT
To your comment above, if you do not have control over super class, i am afraid, you can not do anything.
The problem is that with generics List<SuperChild> and List<SubChild> are not compatible, since if you'd call getList() on a SubParent instance but through a SuperParent interface, you'd get a return value of type List<SuperChild>. This would allow you to add other instances of SuperChild even though the list is only allowed to contain instances of SubChild (as per the return type defined in SubParent).
To make this compile change the return type to List<? extends SuperChild>, i.e.
public class SuperParent
{
public List<? extends SuperChild> getList()
{
return new ArrayList<SuperChild>();
}
}
This would allow you to return lists of subtypes but would not allow you to add elements to the list returned using the super type (i.e. you can't add elements to a List<? extends SuperChild>.
List<SubChild> is not an subclass of List<SuperChild>
There is no co-variance in java's generics.
So, when you try to co-variant the return type, it is actually a different type, and java does not allow you to change it completely [since it will not be safe].
Your method getList() in SubParent should return List<SuperChild> [or ArrayList<SuperChild>, ...] to solve this issue.
As others pointed out List<SubChild> is not a subclass of List<SuperChild>.
Depending on what you want to do, you could use generics:
public class SuperParent<T extends SuperChild>
{
public List<T> getList()
{
return new ArrayList<T>();
}
}
public class SuperChild
{
}
public class SubParent extends SuperParent<SubChild>
{
public List<SubChild> getList()
{
return new ArrayList<SubChild>();
}
}
public class SubChild extends SuperChild
{
}
Imagine something like this:
SubParent subParent = new SubParent();
SuperParent superParent = (SuperParent) subParent; // upcast is okay
List<SuperChild> list = superParent.getList();
list.add(new SuperChild());
The last statement would violate the contract of SubParent.
A fix would be to change the contract of SuperParent's getList to List<? extends SuperChild> getList().
Why are java generics so tricky? I thought I finally understood, but eclipse gives me an error at the line in somOtherMethod below using either of the getOuterList methods below.
protected List<?> getOuterList() {
// blah blah
}
protected List<? extends Object> getOuterList() {
// blah blah
}
protected void someOtherMethod() {
...
getOuterList().add((MyObject)myObject); //compile error
...
}
UPDATE: ok - so I understand the error now. It was lack of understanding on my part of what List<?> or List<? extends SomeObject> really means. In the former case, I thought it meant a list that could contain anything. In the latter case, I assumed it was a list of a bunch of objects that extend SomeObject. The proper representation of my understanding would just be List<Object> and List<SomeObject> (w/out the extends). I thought extends helped me solve a problem which they don't. So here's where my real problem lies:
public interface DogKennel {
public List<Dog> getDogs();
}
public class GreyHoundKennel implements DogKennel {
protected List<GreyHound> greyHounds;
public List<GreyHound> getGreyHounds() {
return this.greyHounds;
}
public List<Dog> getDogs() {
// Is there no way to handle this with generics
// w/out creating a new List?
return getGreyHounds(); //compiler error
}
}
This declaration:
List<?> getOuterList() { }
is telling the compiler "I really don't know what kind of list I'm going to get back". Then you essentially execute
list<dunno-what-this-is>.add((MyObject)myObject)
It can't add a MyObject to the List of something that it doesn't know what type it is.
This declaration:
protected List<? extends Object> getOuterList() { ... }
tells the compiler "This is a list of things that are subtypes of Object". So again, of course you can't cast to "MyObject" and then add to a list of Objects. Because all the compiler knows is that the list can contain Objects.
You could however, do something like this:
List<? super MyObject>.getOuterList() { ... }
and then successfully add a MyObject. That's because now the compiler knows the List is a list of MyObject, or any supertype of MyObject, so it can surely accept MyObject.
Edit: As for your DogKennel example, this code snippet I think does what you want:
protected List<GreyHound> greyHounds;
// We only want a List of GreyHounds here:
public List<GreyHound> getGreyHounds() {
return this.greyHounds;
}
// The list returned can be a List of any type of Dog:
public List<? extends Dog> getDogs() {
return getGreyHounds();
}
You are saying that the method returns a "List of some unknown type" (which you can't add to, because you can't guarantee that the thing you are adding is a subtype of that type). You actually want to say, a "List of whatever type you want", so you have to make the method generic:
protected <T> List<T> getOuterList() {
// blah blah
}
Okay, I just looked at your update:
It all depends on what you intend to be able to do with the result of getDogs(). If you do not intend to be able to add any items to the list, then getDogs() should return type List<? extends Dog>, and then the problem would be solved.
If you intend to be able to add things to it, and by the type List<Dog> it means that you can add any kind of Dog to it, then logically this list cannot be the same list as greyHounds, because greyHounds has type List<GreyHound> and so Dog objects should not go in it.
Which means that you must create a new list. Keeping in mind of course that any changes to the new list would not be reflected in the original list greyHouds.
You're tripping over the fact that Java generics are not polymorphic on the type parameter.
Talking through your code fragment, let's pull the example apart:
protected List<GreyHound> greyHounds; // List<GreyHound> is fine
/** This method returns a lovely List of GreyHounds */
public List<GreyHound> getGreyHounds() {
return this.greyHounds;
}
/** Here is the problem. A List<GreyHound> is not a List<Dog> */
public List<Dog> getDogs() {
return getGreyHounds(); //compiler error
}
So your original comment is correct. The two Lists are definitely different with no inheritance between them. So, I would suggest that you investigate these two options:
Try returning a new list as you suggest in your comment. For example, return new ArrayList<Dog>(this.greyHounds);
Do you really need to keep a list of a specific breed of Dog? Perhaps you should define the data member to be a List<Dog> to which you add your specific GreyHounds. I.e., protected List<Dog> greyHoundsOnly; where you manage which dogs are allowed in the kennel via the object's external interface.
Unless you have a good reason to keep a type-specific list, I would think seriously about option 2.
EDIT: fleshing out my suggested options above:
Option 1: Return a new list. Pros: Simple, straightforward, you get a typed list out and it eliminates a thread-safety problem (doesn't expose an internal reference to the world). Cons: seemingly a performance cost.
// Original code starts here.
public interface DogKennel {
public List<Dog> getDogs();
}
public class GreyHoundKennel implements DogKennel {
protected List<GreyHound> greyHounds;
public List<GreyHound> getGreyHounds() {
return this.greyHounds;
}
// Original code ends here
public List<Dog> getDogs() {
// This line eliminates the thread safety issue in returning
// an internal reference. It does use additional memory + cost
// CPU time required to copy the elements. Unless this list is
// very large, it will be hard to notice this cost.
return new ArrayList<Dog>(this.greyHounds);
}
}
Option 2: Use a different data representation. Pros: plays nicer with polymorphism, returns the generic list that was the original goal. Cons: it's a slightly different architecture which may not fit with the original task.
public abstract class DogKennel {
protected List<Dog> dogs = new ArrayList<Dog>();
}
public class GreyHoundKennel extends DogKennel {
// Force an interface that only allows what I want to allow
public void addDog(GreyHound greyHound) { dogs.add(greyHound); }
public List<Dog> getDogs() {
// Greatly reduces risk of side-effecting and thread safety issues
// Plus, you get the generic list that you were hoping for
return Collections.unmodifiableList(this.dogs);
}
}
a generic type of ? means "some specific type, but i don't know which". anything using a ? is essentially read-only because you can't write to it w/out knowing the actual type.
There is already an accepted answer, however, pls consider the following code modification.
public interface DogKernel {
public List<? extends Dog> getDogs();
}
public class GreyHoundKennel implements DogKernel {
protected List<GreyHound> greyHounds;
public List<GreyHound> getGreyHounds() {
return this.greyHounds;
}
public List<? extends Dog> getDogs() {
return getGreyHounds(); // no compilation error
}
public static void main(String[] args) {
GreyHoundKennel inst = new GreyHoundKennel();
List<? extends Dog> dogs = inst.getDogs();
}
}
Java generics are indeed broken, but not that broken.
BTW Scala fixes this in a very elegant way by providing variance handling.
UPDATE ----------
Please consider an updated snippet of code.
public interface DogKennel<T extends Dog> {
public List<T> getDogs();
}
public class GreyHoundKennel implements DogKennel<GreyHound> {
private List<GreyHound> greyHounds;
public List<GreyHound> getDogs() {
return greyHounds; // no compilation error
}
public static void main(String[] args) {
GreyHoundKennel inst = new GreyHoundKennel();
inst.getDogs().add(new GreyHound()); // no compilation error
}
}