Ethan Wu
Ethan Wu

Reputation: 13

Abstract Class and Abstract Method

I'm writing a program where I create an abstract class Bird. In this class there is a method called chirp() which prints out "chirp". I made an 2 additional classes called Goose and Mallard that extend from Bird with overriding chirp methods that print out different sounds: "Honk" & "Quack". If I was to add another class called Crow that extends from Bird how would I write a method chirp that doesn't overriding?

My approach is to make a method chirp with a different method signature. I think did this but its not compiling as I want it.

Additionally, if I were to make the method in Bird abstract what changes would I need to make to the crow class...

Here is the code I have so far:

import java.util.Scanner;

public abstract class Bird {

    public void chirp() {
        System.out.println("chirp");
    }

    //if chirp made abstract
    public abstract void chirp();

}

public class Goose extends Bird {

    public void chirp() {
        System.out.println("Honk");
    }

}

public class Mallard extends Bird {

    public void chirp() {
        System.out.println("Quack");
    }

}

public class Crow extends Bird {

String sound;

public void chirp(String x) {
    sound = x;
} 

}

public class Problem10 {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        //Demonstration of polymorphism
        Bird goose = new Goose();
        Bird mallard = new Mallard();
        goose.chirp();
        mallard.chirp();


        //Reference Variable
        Bird bird = null;

        //Prompt the user to enter a number that is either 1 or 2
        Scanner scanner = new Scanner(System.in);
        System.out.println("Input number either 1 or 2: ");
        int inputInt = scanner.nextInt();
        if (inputInt == 1) {
            bird = new Goose();
        }
        else if (inputInt == 2) {
            bird = new Mallard();
        }
        else if (inputInt == 3) {
            bird = new Crow();
            bird.chirp("Crow");
        }
        bird.chirp();
    }

}

Upvotes: 0

Views: 390

Answers (4)

Zymus
Zymus

Reputation: 1698

I think that the solution here isn't about inheritance, but rather naming.

Not every Bird can chirp. For example, a Crow might kaw and a Duck might quack.

This should tell you that a Bird must not be allowed to chirp if you also want a Duck to quack, or a Crow to kaw.

Instead, you should think of a chirp as a type of Noise. All noise-making birds have a noise that they make (otherwise they wouldn't be noise-making birds).

So to codify this, you might have something like

public interface Noise {
    void makeNoise();
}

public class Chirp extends Noise {

    public void makeNoise() {
        System.out.println('Chirp!');
    }
}

public class Quack extends Noise {

    public void makeNoise() {
        System.out.println('Quack!');
    }
}

Once you have your noises, you have a couple of options for the birds. You could only have a Bird class, and what type of noise it takes determines what type of bird it is, like

public class Bird {
    private final Noise noise;

    public Bird(Noise noise) {
        // ...
    }

    public final void makeNoise() {
        noise.makeNoise();
    }
}

With the client code something like

Bird duck = new Bird(new Quack());
Bird robin = new Bird(new Chirp());

The other option extends this a little bit by declaring classes that know what type of noise they'll use.

public class Duck extends Bird {

    public class Duck() {
        super(new Quack());
    }
}

Which is used like

Bird duck = new Duck();

I would say to prefer the first option, unless you have concepts which can really really only be applied to a Duck, and the other Birds simply do not have that concept.

Upvotes: 0

Lew Bloch
Lew Bloch

Reputation: 3433

If you want to invoke a method in a Crow instance that's referenced by a Bird variable,

Bird crow = new Crow();

you will need to have the method defined in Bird and overridden in Crow. Variable type (Bird) determines at compile time what method the variable can reach. Object type (Crow) determines at run time which possible override will actually run. So if you want Bird crow to call chirp(String), such a method must exist in Bird.

You could work around that by having the sound set in Crow via a constructor, a setter, or an auxiliary method.

Bird bird = new Crow("croak");
bird.chirp();

or

Crow crow = new Crow();
crow.setSound("croak");
Bird bird = crow;
bird.chirp();

or

public class Crow {
  public void chirp() {
    play(getSound());
  }

  private Sound getSound() {
    Sound sound = // Figure out what sound
    return sound;
  }
}

The choice depends on how dynamic the determination of the sound needs to be.

Upvotes: 0

Makoto
Makoto

Reputation: 106508

I think you're about 99% there from the code you've written. Here's why:

Overloaded methods - methods with the same name but different parameter lists - will not conflict with each other.

Case in point, if you leave your abstract class with the defined, concrete method of chirp, or if you had only defined the abstract method, then you only need to add this to your Crow class to get it to chirp properly:

public class Crow extends Bird {

    String sound;

    // Use this to actually refer to Crow's chirp sound defined prior
    public void chirp() {
        System.out.println(sound);
    }

    public void chirp(String x) {
        sound = x;
    }

}

You can create an overloaded method just fine, but given that you're extending from an abstract class, you are still responsible for handling the call hierarchy based on what you want to accomplish. If Crow defined its own way to get a sound based on a field in it, then you're responsible for populating that field on that instance of Crow.

Upvotes: 1

Dave Cousineau
Dave Cousineau

Reputation: 13198

You have three choices for the abstractness:

  • Bird is abstract, but chirp is not: this means there's is no such thing as a concrete "Bird". "Bird" is a general concept grouping together like things. The fact that chirp is concrete means that all "Birds" will default to saying "chirp" if they don't decide to override it.

  • Bird is abstract and so is chirp: Same as above, except now there is no default "chirp"; anything that is a "Bird" must come up with its own way of chirping.

  • Bird is not abstract and neither is chirp: in this case, now there do exist such things as true concrete "Birds" that are distinct from more derived things like "Crows" and "Ducks". All true "Birds" say "chirp" and more derived kinds of "Birds" will default to saying "chirp" unless they decide to override it.

In all three of these cases, you can treat more derived kinds of "Birds" as Birds, even when there aren't true concrete Birds.

When overriding you have various choices as well:

  • Don't override: you get the default behaviour as explained above. This is not an option if the method is abstract.

  • Shadow: this means the more derived type has a method of the same name as the base type, but it doesn't actually "override" it. In this case, if you treat a Goose as a Goose it will say "honk", but if you treat a Goose as a Bird it will say "chirp". In other words, the method that gets called depends on the reference you used to call it. (I'm not 100% sure you can do this in Java, and haven't been able to find an example so far.)

  • Override: this means you did actually override the method. Even if you treat a Goose as a Bird, it will still say "honk". This is called polymorphism, because now you can have a list of "Birds", and even though you're treating them as Birds, they will all perform their task in their own unique overridden way.

Upvotes: 1

Related Questions