Shane T
Shane T

Reputation: 123

Trouble running methods on objects of a polymorphic array in Java

I was given 2 classes for the assignment: Sale and DiscountSale (which extends Sale). I'm supposed to create a new class called MultiItemSale which will create an array (shopping cart) of both Sale and DiscountSale objects. But I can't get methods from DiscountSale to work on DiscountSale ojbects within the array.

class Sale (the base class) has some methods, setName() and setPrice() in particular.

class DiscountSale extends Sale, so it can use setName() and setPrice(), but it also has setDiscount() among other things.

in MultiItemSale:

Sale[] shoppingCart = new Sale[numOfItems];

From my understanding, since DiscountSale extends Sale, both Sale and Discount Sale objects should be able to placed within this array, correct?

I use a for loop to ask if an item is discounted. If it isn't then:

shoppingCart[i] = new Sale();

if it is discounted:

shoppingCart[i] = new DiscountSale();

And this is where I begin to run into issues:

The following works, because setName() and setPrice() are from the Sale class

Also, this is all under an if-statement that says if item is discounted, then:

shoppingCart[i] = new DiscountSale();
shoppingCart[i].setName(name);
shoppingCart[i].setPrice(price);

But if I try to do this I get errors because setDiscount() is from DiscountSale:

shoppingCart[i].setDiscount(discount);

Eclipse tells me, "The method setDiscount(double) is undefined for the type Sale". If

shoppingCart[i] = new DiscountSale();

why can't I use a method from DiscountSale on that object? I think I have a misunderstanding of how polymorphism and arrays work.

Upvotes: 2

Views: 255

Answers (2)

scottb
scottb

Reputation: 10084

In the parlance of object-oriented design, a DiscountSale is a Sale.

Normally, a program would collect Sale and DiscoutSale objects into the same Sale collection in order to perform common Sale processing on those objects in the collection defined by the Sale API. So it doesn't make a lot of sense that you would try to use a statement like:

Sale[] shoppingCart = new Sale[] { ... };
    :
    :
shoppingCart[i].setDiscount(...);

Because a Sale isn't a DiscountSale, setting a discount rate on a top level Sale item, as you've designed it, isn't part of its API and the statement doesn't make a lot of sense.

DiscountSale objects will inherit the methods from Sale (a DiscountSale is a Sale), but Sale objects will have no knowledge of any subclass-specific methods defined in DiscountSale (a Sale object doesn't have to be a DiscountSale).

So what to do?

A common solution to a problem like this is to take advantage of polymorphism. For example, in your Sale class make a method such as getNetPrice() part of its API.

public class Sale {

    protected double price;
    :
    :
    public double getPrice() { return price; } // return original price

    public double getNetPrice() { return price; } // return discounted price
    :
    :
}

Notice that the two methods in Sale are identical. How come? Because their contracts are different. The first returns the original price. The second is specified to return the discounted price. Because Sale objects have no discount, it simply returns the original price as well.

However, the DiscountSale class would take advantage of polymorphism to fulfill the API contract:

public class DiscountSale extends Sale {

    private double discountRate; // specified as percent off (0.0 to 1.0)
    :
    :

    @Override
    public double getNetPrice() { 
        return (super.price - (super.price * discountRate)); 
    }
    :
    :
}

Here, DiscountSale objects are defined so that the discount rate is set at the time the object is created. This is pretty straightforward since, as we've said, setting a discount rate on Sale objects in the collection (which may or may not be discounted) is more confusing.

The DiscountSale uses the Sale contract and overrides the getNetPrice() method to calculate and return the discounted price. In this way, both objects can be in the same collection and share a common API to get at the nondiscounted or discounted prices.

Upvotes: 1

cxw
cxw

Reputation: 17041

All the compiler knows is that you have Sales, so those are the only methods you can use. Polymorphism is for the situation when you have the same function calls on different kinds of objects and the same function call will behave differently depending on what kind of object it is.

In this example, name, price, and discount could be parameters of your constructor. That way you could pass them when you say new, and you wouldn't have to call a setting method afterwards.

Edit: Looking at the Wikipedia article on the Circle-Ellipse Problem, it occurs to me that any Sale has an implicit discount of 0%. You could use only DiscountSales and be able to access all their members, perhaps including a flag indicating whether the discount can be changed.

Upvotes: 2

Related Questions