0

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?

Tunaki
  • 132,869
  • 46
  • 340
  • 423
caseylouisee
  • 103
  • 1
  • 12
  • Post the signature of `getSquare`. – m0skit0 Nov 05 '15 at 15:27
  • What should happen if the square you are looking at is not a `PropertySquare` and you try and call `getPrice()` on it? – khelwood Nov 05 '15 at 15:27
  • 1
    you know about casting? – Stelium Nov 05 '15 at 15:28
  • 2
    this is missing the point of subtype polymorphism, see [what is the Liskov substitution principle](http://stackoverflow.com/q/56860/217324) : "Functions that use pointers or references to base classes must be able to use objects of derived classes without knowing it." – Nathan Hughes Nov 05 '15 at 15:28
  • Liskov in other words: there is no more point in using subclasses If you have to use `instanceof` since you could as well use arbitrary classes with a `List`. – zapl Nov 05 '15 at 15:47
  • is this following on to the point of I should use interfaces instead? – caseylouisee Nov 05 '15 at 15:51
  • @zapl `List` is definitely not the same as `List` for readability purposes. – m0skit0 Nov 05 '15 at 16:04
  • @m0skit0 Why limit yourself to `Square` when you don't use it? – zapl Nov 05 '15 at 16:08
  • @zapl How "you don't use it"? I don't get your point. All elements in that list are `Square`s, why would you use `Object`? For readability purposes one should always use the most specific type. – m0skit0 Nov 05 '15 at 16:55
  • @m0skit0 The point is that once you use `instanceof`, you could skip having a common type altogether and stay with `Object`. I'm not saying that this is a great idea, I'm saying: at that point, your code is so terrible, why do have that type if you're not using it properly? – zapl Nov 05 '15 at 16:59
  • @zapl Still not the same. Base class has methods you can use for your case, whereas Object doesn't. You shouldn't throw off a code just because of it is using `instanceof`. [`instanceof`](http://stackoverflow.com/questions/2750714/is-instanceof-considered-bad-practice-if-so-under-what-circumstances-is-instan) has its fair uses. What would you suggest as a solution/design instead of `List`? – m0skit0 Nov 05 '15 at 17:08
  • @m0skit0 Would redesign `Square` or use a different interface. When you have to use `instanceof` you're using the wrong abstraction(s). Maybe make use of patterns like [double dispatch](https://en.wikipedia.org/wiki/Double_dispatch) that allow you to interact with different types differently by letting them decide how to deal with you. – zapl Nov 05 '15 at 17:26
  • Let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/94404/discussion-between-m0skit0-and-zapl). – m0skit0 Nov 06 '15 at 08:37

5 Answers5

2

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 (a.k.a. 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();
}
m0skit0
  • 25,268
  • 11
  • 79
  • 127
0

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, i.e. if a Utility has a different method you wish to call.

Jeff Watkins
  • 6,343
  • 16
  • 19
  • Thank you this helped me a lot. I may change everything and use interfaces however for now the instanceof method works. – caseylouisee Nov 05 '15 at 15:49
0

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.

Hendrik Simon
  • 231
  • 2
  • 10
0

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..
Stelium
  • 1,207
  • 1
  • 12
  • 23
0

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

kstandell
  • 775
  • 6
  • 16