Extra exercise on inheritance

From Juneday education
Jump to: navigation, search

Description

As per request by our readers, here's another exercise on inheritance.

The focus on this exercise is rules and syntax, so please accept our apologies if the example is kind of construed!

Managing constructors

It is important to know that every constructor always starts its execution by calling the constructor of the super class (the class it is extending). Even if we don't write code for this ourselves, the compiler sneaks in a piece of code calling the super class parameterless constructor as if we'd explicitly written super();

If there is no parameterless constructor in the superclass, obviously this won't compile!

So what to do?

We can add an explicit call to one of the existing constructors in the superclass. Let's look at an example:

public class TextArtifact{
  private String title;
  private String author;
  private int numberOfPages;
  public TextArtifact(String title, String author, int numberOfPages){
    this.title  = title;
    this.author = author;
    this.numberOfPages = numberOfPages;
  }

  String title() { return title;  }
  String author(){ return author; }
  int numberOfPages(){ return numberOfPages; }

  @Override
  public String toString(){
    return title + " by " + author + " (" + numberOfPages + " pages)";
  }
}

public class Book extends TextArtifact{
  String coverType;
  public Book(String title, String author, int numberOfPages){
    super(title, author, numberOfPages);
  }
  public Book(String title, String author, int numberOfPages, String coverType){
    super(title, author, numberOfPages);
    this.coverType = coverType;    
  }
  public String coverType(){ return coverType; }
  @Override
  public String toString(){
    return super.toString() + " cover: " + coverType;
  }
}
public class TestTextArtifact{
  public static void main(String[] args){
    TextArtifact book = new Book("Java", "Mr Bean", 345, "Paper back");
    System.out.println(book); 
  }
}

Exercise 1 - create the text classes

Create the three classes above and make sure they compile and that the test program runs with the expected output.

Exercise 2 - make the Book class not compile

Try to comment out the calls to super() in the Book class and compile. You will get the following error message from the compiler.

$ javac TestTextArtifact.java && java TestTextArtifact
./Book.java:3: error: constructor TextArtifact in class TextArtifact cannot be applied to given types;
  public Book(String title, String author, int numberOfPages){
                                                             ^
  required: String,String,int
  found: no arguments
  reason: actual and formal argument lists differ in length
./Book.java:6: error: constructor TextArtifact in class TextArtifact cannot be applied to given types;
  public Book(String title, String author, int numberOfPages, String coverType){
                                                                               ^
  required: String,String,int
  found: no arguments
  reason: actual and formal argument lists differ in length
2 errors

The message looks long and scary but don't worry. Try to learn and recognize the message. What it says that is important is the following part:

error: constructor TextArtifact in class TextArtifact cannot be applied to given types
required: String,String,int
found: no arguments

So, the compiler complains about finding a call to the constructor of TextArtifact (Book's superclass), with no arguments. It expected a call to the superclass' constructor instead with String,String,int (which is the only available constructor in fact).

The reason for this unexpected error message is that, actually, the compiler makes sure that there's always a call to the superclass' constructor as the first instruction of a subclass constructor. If there is no such call in your code, it inserts one - but then it always inserts a call to a parameterless constructor!

If there is no such parameterless constructor, the compilation later fails, because the call can't be found to match any real constructor in the superclass.

Note that the way to fix this, in most cases, is NOT to add a parameterless constructor in the superclass! This allows for the creation of uninitialized objects of the class. We don't want to have Books or other TextArtifacts whith title and author of the value null (the default value for reference variables) and a numberOfPages set to 0.

The way to fix this is to make sure that you always write an explicit call to the superclass constructor using super() and the correct parameters - parameters of the correct type in the correct order. In the example above, we made the explicit call from the Book constructors to the superclass like this:

super(title, author, numberOfPages);

The call to super() must be the first instruction in the body of a constructor. It typically forwards some or all of the parameters to the current constructor (in this case the Book constructor) to one of the constructors in the parent class.

Exercise 3 - add a new text artifact class

Fix the constructors of the Book class (remove the comments so that it compiles again). Write another class which extends TextArtifact called ComicBook. A comic book should have a title, author and number of pages like every TextArtifact. It should additionally have an artist (the one who drew the paintings) and a string for the issue (for instance "Episode 4, 1984").

Start by adding the following statements to the TestTextArtifact program:

TextArtifact spider = new ComicBook("The amzing Spider-Man",
                                    "Stan Lee", 30,
                                    "Episode 1, 1963",
                                    "Kirby/Ditko");
System.out.println(spider);

If you compile the test program it will of course complain about the lack of a ComicBook class. So write it!

  • It must extend TextArtifact
  • It must have at least one constructor which sets all members
    • title, author, numberOfpages, issue, artist
  • That constructor must call the superclass' constructor with the common members
    • title, author, numberOfPages
  • Add methods for retrieving the String for issue and artist respectively
    • The methods should have descriptive names
    • The methods should take no arguments
    • The methods should return the reference to the respective member (issue resp. artist)
  • Override toString() so that it
    • calls the superclass' version of toString()
    • appends information about issue and artist
  • If you put your text artifact classes in packages (like you normally would) think about access modifiers
    • Member variables should be private
    • Constructors should be public
    • methods to be used from client code like the test program should also be public

Note that when you end up with this amount of arguments to a constructor, you should consider using a Builder instead, but that's outside the scope of this exercise - so we'll stick with the clumpsy constuctor with five arguments.

Below you can sneak-peek on a suggested solution.

Suggested solution to the ComicBook class

Expand using link to the right to see the full content.

public class ComicBook extends TextArtifact{
  private String artist;
  private String issue;
  public ComicBook(String title, String author, int numberOfPages,
                   String issue, String artist){
    super(title, author, numberOfPages);
    this.artist = artist;
    this.issue = issue;
  }
  public String issue(){ return issue; }
  public String artist(){ return artist; }
  @Override
  public String toString(){
    return super.toString() + 
      " issue: " + issue + " artist: " + artist;
  }
}
/*
Output from running the test program:
$ javac TestTextArtifact.java && java TestTextArtifact
Java by Mr Bean (345 pages) cover: Paper back
The amzing Spider-Man by Stan Lee (30 pages) issue: Episode 1, 1963 artist: Kirby/Ditko
*/