3

Given the follow code:

public class Musician {

    public void play() {
        // do something
    }
}

.

public class Drummer extends Musician {

    public void turnsDrumStick() {
        // do something
    }
}

.

public class Guitarist extends Musician {

    public void strummingStrings() {
       // do something
    }
}

I can use polymorphism to do the following:

    Musician m1 = new Guitarist();
    Musician m2 = new Drummer();

    m1 = m2;

However, I can't see the methods of the subclass:

    m1.strummingStrings(); //COMPILATION ERROR!

If I use:

Guitarist m1 = new Guitarist();

Would not it be better? What is the advantage of using Musico type to reference an object of a subclass? Just a possibility I would be able to attribute m1 = m2;, for example? Or are there other advantages?

I saw this post, but I'm still puzzled: Using superclass to initialise a subclass object java

deldev
  • 1,296
  • 18
  • 27
  • Another advantage is that if you want to put all musicians into one data structure (like a list), you can do it, without the superclass you can't. – BackSlash Feb 11 '14 at 17:12
  • Related to http://stackoverflow.com/questions/383947/what-does-it-mean-to-program-to-an-interface. Not sure whether I'd call it a duplicate or not though. – Jon Skeet Feb 11 '14 at 17:12
  • 2
    the title of your question is incorrect. It should be something like *"Why use a superclass reference for an object"* – UmNyobe Feb 11 '14 at 17:13
  • Examples of polymorphism often look over-cooked and superfluous in isolated snippets such as this. You need to see a larger, well designed, code-base in action to appreciate these subtleties. – mungflesh Feb 11 '14 at 17:17
  • @BackSlash, I don't understood you, because if I have a List list = new ArrayList(), I can put Guitarist or Drummer, even though I have created both as subclass. – deldev Feb 11 '14 at 17:23
  • @dellasavia Read my comment. I said that you can't do like that if you don't have a **super** class. If your Guitarist and Drummer didn't have a common superclass, you couldn't put them in the same List (unless you declare the list as `List`) – BackSlash Feb 11 '14 at 17:27
  • @BackSlash, but if them didn't have a commom superclass, I also could not create a object using your superclass, correct? – deldev Feb 11 '14 at 17:33

5 Answers5

6

The advantage of polymorphism comes when you can call play() on any Musician, whether it's a Guitarist, a Drummer, or any other subclass of Musician that you may not have created yet.

Musician m1 = new Guitarist();
Musician m2 = new Drummer();
m1.play();
m2.play();

This could output something like

Guitarist strumming
Drummer drumming

if you override the play() method in both of your subclasses. This way, the code that calls play() doesn't need to know which implementation of Musician it really is, just that it's a Musician and it's guaranteed to have a play() method.

It is not an advantage of polymorphism to be able to call a subclass method such as strummingStrings from a superclass reference, because the method only exists in the subclass. It's not guaranteed to exist in the superclass. If you need to call a subclass-only method such as strummingString, then you need a subclass reference.

You could define strummingStrings in the superclass Musician, and polymorphism would work, but that would be a bad design. Not all musicians can strum strings on a guitar.

rgettman
  • 176,041
  • 30
  • 275
  • 357
2

Good question, but the problem you are having is inherent of your design.

If you do something like this:

public class Musician {

    public void play() {
        // do something
    }
}
.

public class Drummer extends Musician {

    public void turnsDrumStick() {
        // do something
    }

    public void play() { 
        //...perhaps some other things....
        turnsDrumStick();
    }
}
.

public class Guitarist extends Musician {

    public void strummingStrings() {
       // do something
    }

    public void play() {
       strummingStrings();
       //other things
    }
}

So the "Band" class consists of a collection of Musicians. The band class tells them when to start playing by calling the play method. The implementation details are left up to the subtypes, which is where the power of inheritance is realized.

Pete B.
  • 3,188
  • 6
  • 25
  • 38
1

If you are writing a method that actually requires and uses Musician, you don't accidentally want to depend on subclass features.

In your example:

 m1.strummingStrings(); //COMPILATION ERROR!

That's a GOOD thing if, say, you're writing a test driver for a class that accepts a Musician.

Now of course, between the beginning of a function and the end of a function in many ways coding standards don't matter as much, since it's possible for the reader and maintainer to gain a total understanding of what is going on. So in the example:

void foo() {
     Guitarist g;
     g.play();
}

and

void foo() {
     Musician m;
     m.play();
}

Will rarely make a big difference.

But

Musician foo() {
// ...
}

and

Guitarist foo() {
// ...
}

Will make a BIG difference. In the former case the client code never is allowed to expect a Guitarist, whereas in the latter code the implementor is forever coupled to returning Guitarists forever, even if in the future it turns out a Banjoist can be implemented and returned more easily, since client code depends on it.

djechlin
  • 59,258
  • 35
  • 162
  • 290
1

Guitarist and Drummer are called specializations of Musician. They perform specific tricks, and they have specific way of playing. If you have a Band, you'd probably have something like that:

public class Band {
    private List<Musician> musicians = new ArrayList<Musician>();
    public void addMusician(Musician m) {
        musicians.add(m);
    }
    public void play() {
        for (Musician m : musicians) {
            m.play();
        }
    }
}

Now, you need to override play in Drummer and in Musician to give then special behaviors:

public class Drummer extends Musician {
    @Override
    public void play() {
        this.turnsDrumStick();
    }
    [...]
}
njzk2
  • 38,969
  • 7
  • 69
  • 107
1

They way you've written your classes, there's really no reason to subclass Musician because neither of the subclasses actually use musicians play() method. The advantage of declaring m1 and m2 as Musicians rather than their respective subclasses is that the instantiating class doesn't need to know how the play() method works; it can just call m1.play() and m2.play() and the subclasses know what to do.

To benefit from that type of flexibility, you'll need to restructure your objects. I'd suggest, at the very least, making Musician abstract, then overwriting the play() method in the subclasses to either strum or drum.

If you want to take it a bit further, you can define a Play interface and implement that interface for classes called StrumGuitar and Drum, thus encapsulating these behaviors. Then, e.g., the Guitarist's play() method could delegate it's behavior to the StrumGuitar class. Further, the instantiating code simply has to call play() without worrying about what it does.

These techniques help to keep your code logically organized, flexible, and allow the type of the subclasses to be unknown to the instantiating class at runtime, which can be a huge benefit to the flexibility of your code.

Justin Pollard
  • 6,661
  • 1
  • 18
  • 20