Chapter:Interfaces - Implementing an interface - Exercises

From Juneday education
Jump to: navigation, search

Exercises on implementing interfaces

We are going to look into some of the existing interfaces in Java. We'll start off with looking into how Java can help you sort objects. First we're looking at how Java sorts String objects and continue with sorting Member objects to learn about the beauty of interfaces and how relatively easy it is to write general methods and classes. One example is the sort function you get with the Arrays class. The method is able sort an array of objects of any type as long as all the objects in the array has the same type. As an example sort can sort String objects, Member objects and classes you'll create in the future. All you need to do is to help the sort method by helping sort to tell the order of two objects.

Q1

Let's start out with sorting String objects. Create a class, Sorter, in the package net.sortex.strings. Write a main method that sorts the Strings passed as arguments to the program (the main method of the class). The sorting should be done using Arrays.sort(). Print the String objects after you've sorted them.

Hint: Arrays has a method called toString which you can pass an array of Object references (e g array of String refereces).

Expand using link to the right to see a suggested answer/solution.

package net.sortex.strings;

import java.util.Arrays;

public class Sorter {

    public static void main(String[] args) {

        Arrays.sort(args);
        System.out.println(Arrays.toString(args));
        
    }
    
}

Q2

How could Java possibly know how to sort the array?

Hint: When you're sorting things in the real world you usually, or at least sometimes, compare two objects to see which one is biggest or they're of the same size. Java does the same thing. In the case of String objects, the Comparable interface is used.

Expand using link to the right to see a suggested answer/solution.

The sort method in the Arrays class sorts an array of Objects if it implements the Comparable interface. To implement this interface a class has to provide an implementation of the method int compareTo(T o), which in the case of String objects translates to int compareTo(String anotherString). If the class implements that method Java can easily sort the objects by simply asking them two-by-two which one is biggest.

Compare this to sorting Students in a class. You have no idea what criteria to use to sort (name, length, age...) but you can still sort the students in order if you had a chance to ask the students two-by-two which one is biggest. This is the way Java does it. By implementing the compareTo interface we can rest assured that Arrays.sort can do its job.

Q3

Create a simple class, Book:

  • with the following instance variables:
    • private String name
  • a public constructor with which you can set the name of the book
  • a public method that returns name
  • which is in the package net.sortex.books

Expand using link to the right to see a suggested answer/solution.

Suggested solution:

package net.sortex.books;

import java.util.Arrays;

public class Book {

    private String name;

    public Book(String name) {
        this.name = name;
    }
          
    public String name() {
        return name;
    }
    
}

Q4

Add a test class TestBook in the package net.sortex.books.test. Add a main method to the class and create 4 Book instances and stores them in an Array. Sort the Book instances using the method sort in Arrays.

Compile and try to execute. ... Uh oh... didn't quite work. Why? Read the error message from the runtime exception

Note: you don't need the Sorter class anymore.

Expand using link to the right to see a suggested answer/solution.

The Arrays.sort() method crashes if the elements of the Array are not Comparable, because the code in the sort method tries to cast each element of the array to a Comparable reference, so it can call the compareTo() method while sorting:

Exception in thread "main" java.lang.ClassCastException: net.sortex.books.Book cannot be cast to java.lang.Comparable
	at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320)
	at java.util.ComparableTimSort.sort(ComparableTimSort.java:188)
	at java.util.Arrays.sort(Arrays.java:1246)
	at net.sortex.books.test.TestBook.main(TestBook.java:14)

Q5

Add a method int compareTo(Book anotherBook) to the Book class. The actual implementation of the method is not important at the moment. Just make sure you return an int value.

Hint: compareTo is a method that is used by Arrays.sort() and Collections.sort() when sorting lists.

Compile. Run.

Still can't sort :(

$ javac net/sortex/books/test/TestBook.java && java net.sortex.books.test.TestBook
Exception in thread "main" java.lang.ClassCastException: net.sortex.books.Book cannot be cast to java.lang.Comparable
	at java.util.ComparableTimSort.countRunAndMakeAscending(ComparableTimSort.java:320)
	at java.util.ComparableTimSort.sort(ComparableTimSort.java:188)
	at java.util.Arrays.sort(Arrays.java:1246)
	at net.sortex.books.test.TestBook.main(TestBook.java:14)

Expand using link to the right to see a suggested answer/solution.

Example of method implementation

    public int compareTo(Book anotherBook) {
        return this.name.compareTo(anotherBook.name());
    }

Java still can't sort the Array. It's because we're not stating that we implement the Comparable interface, so the sort method still generates a class cast exception when it tries to cast a Book to a Comparable.

Q6

State that your class is implementing the Comparable interface.

Hard to see if it worked, isn't it? Compile and run. If the sort method in Arrays doesn't crash anymore, it is at least a step in the right direction.

Expand using link to the right to see a suggested answer/solution.

Suggested solution:

public class Book implements Comparable<Book> {
  // ... same as before, including the compareTo(Book other) method etc
$ javac net/sortex/books/test/TestBook.java && java net.sortex.books.test.TestBook
$ # Yey! No runtime exception!

Q7

Write a method in the Book class that:

  • can return a String representation of a Book object
  • is automatically called by for example System.out.println

Hint: implement public String toString().

Add a statement to TestBook before and after the call to Arrays.sort which prints the array to the standard out stream:

// If your array is called books, then:
System.out.println(Arrays.toString(books));
Arrays.sort(books);
System.out.println(Arrays.toString(books));

The Arrays.toString() method is a convenient way to make a String from a whole array. It adds a "[" and then calls toString() for each element, followed by , except for the last element, and then ends the String with "]"

Ok, it works kind of good now. So the method sort in Arrays can sort objects for you, as long as you define a method (compareTo) that sort can use to compare two objects. That's all you do and the already written method sort does the actual sorting. Nice, isn't it. But how does sort do the sorting?

Are we happy now? No, let's add some more stuff to Book.

Expand using link to the right to see a suggested answer/solution.

Suggested solution:

package net.sortex.books.test;

import net.sortex.books.Book;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Arrays;

public class TestBook {

    public static void main(String[] args) {
        Book[] books = { new Book("ABC ... and more"),
                         new Book("A book"),
                         new Book("A book"),
                         new Book("Book again"),
                         new Book("Another book")
        };

        System.out.println(Arrays.toString(books));        
        Arrays.sort(books);
        System.out.println(Arrays.toString(books));        
    }

}
package net.sortex.books;

public class Book implements Comparable<Book> {

    private String name;

    public Book(String name) {
        this.name = name;
    }
          
    public String name() {
        return name;
    }

    public int compareTo(Book anotherBook) {
        return this.name.compareTo(anotherBook.name);
    }

    public String toString() {
        return name;
    }
    
}

sort can sort objects just as you can sort elements of any kind if you were allowed to check the order of two objects. Exactly how it performs the sorting isn't super interesting here. But we should realize that in order to sort a list or array, we need to be able to compare an element with another element, and that's exactly what our compareTo() method provides to the sorting method.

$ javac net/sortex/books/test/TestBook.java && java net.sortex.books.test.TestBook
[ABC ... and more, A book, A book, Book again, Another book]
[A book, A book, ABC ... and more, Another book, Book again]

Q8

Add, to Book, the instance variable

  • int year

This means you need to rewrite your constructor with two additional parameters. You also "may" need to update the toString method.

Expand using link to the right to see a suggested answer/solution.

See next suggested solution

Q9

Imagine we want to sort Books according to this order:

  • name, and if names are the same:
  • year

Add code to the compareTo method to reflect this.

Soon, we'll ask you to compare books by first looking at year, then, if years are the same, by name. But this will be in the next exercise (where you will create a book-comparator class).

Expand using link to the right to see a suggested answer/solution.

Suggested solution:

package net.sortex.books;

public class Book implements Comparable<Book>  {

    private String name;
    private int    year;

    // To be used between name and year in the toString
    private static final String SEPARATOR = ";";

    public Book(String name, int year) {
        this.name   = name;
        this.year   = year;
    }
          
    public String name() {
        return name;
    }

    public int year() {
        return year;
    }

    public int compareTo(Book anotherBook) {
        // Save the result of only comparing this name with the other's name
        int nameCheck = this.name.compareTo(anotherBook.name);

        // if we have the same name, then we should compare the years
        if (nameCheck == 0) {
            // Simply using minus satisfies the requirements that:
            // * a negative value means this year is less than other's year
            // * zero means this year is the same as other's year
            // * a positive value means that this year is greater than other's year
            return this.year - anotherBook.year;
            /*                                                                        
             * Perhaps the above code is easier understood if presented this way:     
             *                                                                        
             * Get the two books' years and store them in separate variables:         
             *   int year        = this.year;                                         
             *   int anotherYear = anotherBook.year;                                
             *                                                                        
             * Ok, we have the two year variables. The                                
             *   return year - anotherYear;                                           
             *                                                                        
             */
        } else {
            // Names were different, so return the result:
            return nameCheck;
        }
    }
    
    // New version of toString()! Include the year and a separator character
    public String toString() {
        return "(" + name + SEPARATOR + year + ")\n";
    }
    
}

Q10

Let's add some more Books. Here's some book you can copy/paste if you want:

        Book[] books = { new Book("ABC ... and more", 1980),
                         new Book("A book", 1980),
                         new Book("A book-3", 1982),
                         new Book("A book-2", 1982),
                         new Book("Book again", 1719),
                         new Book("Book again", 1898),
                         new Book("Book again", 2013),
                         new Book("Book again - 3", 1984),
                         new Book("Book again - 2", 1985),
                         new Book("Another book", 1990)
        };

Compile and execute to make sure it works.

Expand using link to the right to see a suggested answer/solution.

$ javac net/sortex/books/test/TestBook.java && java net.sortex.books.test.TestBook
[(ABC ... and more,1980)
, (A book,1980)
, (A book-3,1982)
, (A book-2,1982)
, (Book again,1719)
, (Book again,1898)
, (Book again,2013)
, (Book again - 3,1984)
, (Book again - 2,1985)
, (Another book,1990)
]
[(A book,1980)
, (A book-2,1982)
, (A book-3,1982)
, (ABC ... and more,1980)
, (Another book,1990)
, (Book again,1719)
, (Book again,1898)
, (Book again,2013)
, (Book again - 2,1985)
, (Book again - 3,1984)
]

Q11

Write a new class, BookYearComparator, that implements Comparator.

This means you need to:

  • import Comparator
  • explicitly state you're implementing Comparator
  • choose the type (hint: if writing a Comparator for Member objects, you use Comparator<Member>... what type, instead of Member, do you think you should specify if you want to compare Book objects?).
  • implement the compare methos

The method compare should compare the year in two Book objects.

Expand using link to the right to see a suggested answer/solution.

Suggested solution:

package net.sortex.books;

import java.util.Comparator;

public class BookYearComparator implements Comparator<Book> {

    public int compare(Book one, Book two) {
        return one.year()-two.year();
    }

}

Note: we don't need to import Book since Book and BookYearComparator are in the same package.

Q12

Add code in your test class' main method to print out the Books after sorting using the BookYearComparator.

Expand using link to the right to see a suggested answer/solution.

Suggested solution:

    public static void main(String[] args) {
        Book[] books = { new Book("ABC ... and more", 1980),
                         new Book("A book", 1980),
                         new Book("A book-3", 1982),
                         new Book("A book-2", 1982),
                         new Book("Book again", 1719),
                         new Book("Book again", 1898),
                         new Book("Book again", 2013),
                         new Book("Book again - 3", 1984),
                         new Book("Book again - 2", 1985),
                         new Book("Another book", 1990)
        };

        System.out.println("Unsorted");
        System.out.println(Arrays.toString(books));

        System.out.println("Sorted - using names and then year");
        Arrays.sort(books);
        System.out.println(Arrays.toString(books));

        System.out.println("Sorted using BookYearComparator - using year");
        Arrays.sort(books, new BookYearComparator());
        System.out.println(Arrays.toString(books));

    }

Q13

Note that our books now can be sorted either after name or year. ... but what about if two Books have the same year? Now we should change our compare method so that it compares the years and if these are the same it should compare the names.

Hint: when the years of the two objects are the same you can compare the names using the compareTo method.

Compile and check if it seem to work.

Expand using link to the right to see a suggested answer/solution.

Suggested solution:

package net.sortex.books;

import java.util.Comparator;

public class BookYearComparator implements Comparator<Book> {

    public int compare(Book one, Book two) {
        if (one.year()==two.year()) {
            // Ok, same year.  How do check the names? .. why don't                                                         
            // use the compareTo method we've already written :)                                                            
            return one.compareTo(two);
        }
        return one.year()-two.year();
    }

}

Bonus - some examples using lambda expressions etc (Java 8)

For the keen student. We will not include this in the exam (if you have this book as course literature), so this is only a bonus section for those who want to learn something extra.

A lambda expression is an expression which can be used in place of an object of a class implementing a "functional interface" - an interface with just one abstract method, like Comparator<T>.

Let's pretend we have the following class:

public class BookNameComparator implements Comparator<Book> {
  @Override
  public int compare(Book b1, Book b2) {
    return b1.name().compareTo(b2.name());
  }
}

Then, these expressions are interchangeable:

  // Expression 1 (using an existing class):
  new BookNameComparator();

  // Expression 2 (using lambda):
  (b1, b2) -> b1.name().compareTo(b2.name());

  // Expression 3 (using method references):
  Comparator.comparing(Book::name);

  // Expression 4 (using an anonymous local inner class):
  new Comparator<Book>() {
    @Override
    public int compare(Book b1, Book b2) {
      return b1.name().compareTo(b2.name());
    }
  }

So we could get a comparator for Book objects comparing name in four ways like this:

Comparator<Book> nameComp1 = new BookNameComparator();

Comparator<Book> nameComp2 = (b1, b2) -> b1.name().compareTo(b2.name());

Comparator<Book> nameComp3 = Comparator.comparing(Book::name);

Comparator<Book> nameComp4 = new Comparator<Book>() {
    @Override
    public int compare(Book b1, Book b2) {
      return b1.name().compareTo(b2.name());
    }
  };

Those comparators are equivalent.

If you'd like to create a combined comparator comparing year first, then (if year is the same) name, you can do like this:

Comparator<Book> nameComparator = (b1, b2) ->
  b1.name().compareTo(b2.name());
Comparator<Book> yearComparator = (b1, b2) ->
  b1.year() - b2.year();
Comparator<Book> yearThenNameComp = yearComparator.thenComparing(nameComparator);

Link

Source code (exercises/solutions)

You can find complete source code to the suggested solutions below in the implementing-interfaces directory in this zip file or in the git repository.

Further reading

Where to go next

Next page is: Interfaces_-_Writing_an_interface

« PreviousBook TOCNext »