-1

I have an Interface Fruit and classes that implement the Interface as follows:

Apple implements Fruit
Banana implements Fruit

I have a legacy method that works off the Interface like

public String checkFruitColor(List<Fruit> fruit)
{
 ....
 ....
 return fruit.getColor();
}

I am trying to pass in List, List

The issue is that the method is not accepting a list of the Apple, Manago. It is asking for an object of Type Fruit.

Any suggestions will be much appreciated.

slim
  • 40,215
  • 13
  • 94
  • 127
Macky
  • 433
  • 2
  • 9
  • 22

5 Answers5

1

The actual question seems to be:

You have

interface Fruit
class Apple implements Fruit
class Banana implements Fruit

Then you have a method

public String checkFruitColor(List<Fruit> fruit)

And you have

List<Apple> apples = //something
List<Banana> bananas = //something

Now, you have made the (common) assumption and List<Apple> is a subclass of List<Fruit> because Apple is a subclass of Fruit.

But, that means we could do

List<Apple> apples = new ArrayList<Apple>();
List<Fruit> fruits = apples //all good as List<Apple> is a subclass of List<Fruit>
fruits.add(new Banana()); //we can add a Banana to a List<Fruit>
final Apple apple = apples.get(0); //a List<Apple> will always have apples.

OOPS!!

So, in fact, a List<Apple> is not related to a List<Fruit> - as far as the compiler is concerned they are completely different. Put in technical terms, List is invariant in its type.

In order to make what you have work, you need to tell the compiler that you want a List of some subtype of Fruit but you don't care what.

public String checkFruitColor(List<? extends Fruit> fruit)

This will allow you to pass your List<Apple> to checkFruitColor and access the items as instances of Fruit.

What you cannot do is add() to the list as you do not know what the type of the List is.

Boris the Spider
  • 59,842
  • 6
  • 106
  • 166
  • 1
    @John my pleasure. But a point for the future would be to triple check your question before posting. Posting the wrong question is waste of your time, and you have also wasted the time of at least 4 other people. – Boris the Spider Jun 11 '14 at 08:51
  • 1
    Yes I am really sorry and will be more careful in future. – Macky Jun 11 '14 at 11:11
  • I'm sorry, but your code example to show why polymorphism wouldn't work on Collections can be applied to arrays, right? So `Apple[] apples = new Apple[0]; Fruit[] fruit = apples; // works because Apple extends Fruit; fruit[0] = new Banana(); final Apple apple = apples[0];` Why would this be OK but your example not? It's just not logical to allow this for arrays but not for collections, even though the outcome (thinking that apples[0] is an Apple but is in fact a Banana) would be exactly the same. Or am I missing something? – klaar Nov 06 '17 at 10:47
  • If my previous comment stands, then I think it's stupid to not allow this for Collections but to still allow it for arrays, since there isn't any logical difference. – klaar Nov 06 '17 at 10:50
  • It would throw an `ArrayStoreException`. It's specifically this nasty behaviour that type invariant generics prevent - they turn a runtime error into a compile time one. @klaar – Boris the Spider Nov 06 '17 at 10:58
  • I understand that having a compile time error is (much more) preferable to having a runtime error. Still, I lament the fact that they somewhat crippled the advantages of polymorphism in collections. If I want to just receive a Collection of any type extending a supertype, it's necessary to use `Collection extends Super>` (or an equivalent Generic type) as the type, instead of just using `Collection`. Using arrays, I could have just typed `Super[]`, so why can't I for Collections? Because they want to catch a silly runtime error during compile time? Bleh. – klaar Nov 06 '17 at 11:55
  • I honestly don't understand your suggestion @klaar. A `Collection` simply **not** a `Collection` because one can `add(banana)` to a `Collection`. There is no alternative to the current implementation. The fact that arrays are fundamentally broken is a problem not a solution. – Boris the Spider Nov 06 '17 at 20:41
  • A collection of apples isn't a collection of fruit indeed, but I should be able to add that collection to another collection of fruit. I should be able to just ask for a collection of any fruit if I only need to use those items as fruit and nothing more specific. I agree about the problem with arrays, but that's the risk involved in using polymorphism: casting from a less to a more specifc Class is something that should be done very carefully, including masking a collection or array to a more generic class and then attempting to use it as a more specific class again without consequences. – klaar Nov 07 '17 at 09:49
0

I assume that you already have a getColor()in your Fruit interface. When a class implements an interface it agrees to the contract of the interface, meaning that it must provide an implementation for all methods in that interface.

Thus, both Apple and Banana will have their own implementation of getColor() and return a correct value. From here on you can just pass an Apple or Banana instance to your check method, since they are both classed as Fruit-instances since they implement Fruit.

Pphoenix
  • 1,423
  • 1
  • 15
  • 37
0

You can pass an instance of Apple or Banana to your method checkFruitColor(Fruit fruit).

Apple apple = new Apple();
apple.setColor("Red");
List<Fruit> fruits = new ArrayList<Fruit>();
fruits.add(apple);
checkFruitColor(fruits); 

Here you cannot directly call fruit.getColor(). You need to iterate through the list and invoke getter for each object.

Dinal
  • 661
  • 4
  • 9
  • Thanks. I am getting a compilation error. I'll need to get back to you on the error as I don't have access to my machine. – Macky Jun 10 '14 at 11:05
  • @John, check the signature of Fruit, see whether it is 'public interface Fruit'. Also cross check whether you have imported the correct Fruit interface in the calling class. – Dinal Jun 10 '14 at 11:12
  • I just remembered that in my actual problem. I was dealing with Lists. Please see the edit I have made to the question. Thanks. – Macky Jun 10 '14 at 12:56
0

Whats the problem in this, you have implemented it corrrectly:-

public String checkFruitColor(Fruit fruit)
{
 return fruit.getColor();
}

Fruit type in parameter restricts the accepted parameter type in the checkFruitColor() function to fruits. Which is implemented by Apple and Banana class, Hence you can pass both Apple and Banana object to it.

You can also pass mango if

Mango implements Fruit 

If you want to get explicitly the apple object again in the method you can do this by

public String checkFruitColor(Fruit fruit)
    {
      Apple apple=(Apple)fruit; //casting the fruit to apple, make sure the object passed is apple only
      return apple.getColor();
    }

But if all fruits have getColor() method, then why do this, your approach is perfect.

Mustafa sabir
  • 4,130
  • 1
  • 19
  • 28
0

This is a guess, but it could have to do with the fact that a variable of type List<Apple> is not valid when passed to a function expecting List<Fruit>. This is due to the way Java handles generics. The method would have to have been defined as expecting a variable of type List<? extends Fruit> and then it would work. See this question: Is List<Dog> a subclass of List<Animal>? Why aren't Java's generics implicitly polymorphic?

Community
  • 1
  • 1
jfrank
  • 723
  • 3
  • 9