Reputation: 123
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
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
Reputation: 17041
All the compiler knows is that you have Sale
s, 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 DiscountSale
s and be able to access all their members, perhaps including a flag indicating whether the discount can be changed.
Upvotes: 2