简体   繁体   中英

Subclass and Superclass methods

I'm creating a monopoly game with different types of squares. Square is the Superclass and PropertySquare is the Subclass .

I've created a loop that rolls the dice and moves the players to a different square. The squares are in an array list and I am able to call the superclass methods on them. However, even if the squares are set to more specific types (subclass of square), I cannot call the subclass methods on them.

This is how I have initialised the squares in a board class.

private static final ArrayList<Square> squares = new ArrayList<Square>(40);
squares.add(1, new PropertySquare("Old Kent Road", 1, 60, "Brown"));
board.getSquare(pos).getName());

getName is a method in the superclass square. However, even if the square is a property type, I cannot call the methods in the PropertySquare class such as getPrice();

How would I change this or be able to call the subclass methods?

I assume board.getSquare() returns a Square , and Square doesn't have any getPrice() method, so you can't call getPrice() over an object declared as Square , even if the instance is actually a PropertySquare (aka polymorphism ). To do so, you have to first cast to the specific subclass. For example:

final Square square = board.getSquare(pos);
if (square instanceof PropertySquare) {
    ((PropertySquare)square).getPrice();
}

NB: I'm going to answer this in the more generic sense, I realise that not all Monopoly Squares have a price... But the problem itself, in code, can be solved in two ways.

If all of your items have a price, you probably want to use an "Abstract" base class.

You then put in the methods in the superclass as

protected abstract int getPrice();

and implement it in your subclass.

So you could have subclasses such as undevelopedSquare, propertySquare, gardenSquare etc.

However, in the case of Monopoly, if only the propertySquare instances have getPrice, you should use

if (square instanceOf PropertySquare) {
    price = ((PropertySquare)square).getPrice();
}

NB2: You also have "utilitySquare"s such as the Waterworks, which would have different properties alongside the price (you can't build a hotel on a utility even though you can buy it).

So in this case, maybe Interfaces would be more appropriate such as:

interface PlayableSquare -> Generic square things, you can land on one for instance

interface SaleableSquare -> has pricing etc

interface BuildableSquare -> can build hotels

And then have your generic as

private static final ArrayList<? extends Square> squares

The PropertySquare would then be:

public class PropertySquare extends AbstractSquare implements SaleableSquare, BuildableSquare

where the Abstract class implements "PlayableSquare". Although that may be a step too far as it would almost just be a marker interface.

you can use instanceOf to check for each interface implementation, ie if a Utility has a different method you wish to call.

You should use Generics with the ArrayList. If your list only contains of type PropertySquare , do it like this:

private static final ArrayList<PropertySquare> squares = new ArrayList<>(40);
squares.add(1, new PropertySquare("Old Kent Road", 1, 60, "Brown"));

Then the list will returns objects of type PropertySquare .

If the list can contain any type of square, do it like this:

private static final ArrayList<Square> squares = new ArrayList<>(40);
squares.add(1, new PropertySquare("Old Kent Road", 1, 60, "Brown"));
Square sq = squares.get(0);
if(sq instanceof PropertySquare){
    PropertySquare pSq = (PropertySquare) sq;
    //now you can use any method of PropertySquare
}

The problem in your code is that your object is of type Square and does not know anything about the methods of any subclass. So you have to do a type cast.

you could handle the landing square in a group of ifs like:

if (square instanceof PropertySquare) {
    ((PropertySquare)square).getPrice();
}else if(square instanceof PrisonSquare) {
//do nothing 
}//etc..

Your declared ArrayList is bound to Square meaning you will have a collection of Square objects and a Square reference when interacting with any items despite at run-time it being an instance of a subclass. This is known as polymorphism.

Due to the fact that the references are of type Square the only methods Java knows about are those declared in Square and any other inherited methods. For you to be able to call methods of a subclass you would need to check whether the reference is pointing to an instance of PropertySquare and then down-cast the reference to PropertySquare . You're then saying, it's okay I know its an instance of PropertySquare so I can safely call the getPrice() method declared in PropertySquare .

if (square instanceof PropertySquare) {
    ((PropertySquare)square).getPrice();
}

Alternatively you can look at the instances class name:

square.getClass().getSimpleName();  // Would just give you PropertySquare
square.getClass().getCanonicalName(); // Would give you the fully qualified name e.g. com.foo.bar.PropertySquare

For more info:

https://docs.oracle.com/javase/tutorial/java/IandI/polymorphism.html

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM