Java Abstract Classes, Interfaces, and Comparable Interface Example, Slides of English

The concept of abstract classes in Java with an example of an abstract Account class and its subclass Fee. It also explains interfaces in Java with the help of an example of the Comparable interface. The Comparable interface is used to sort objects and is demonstrated with an Ingredient class example.

Typology: Slides

2021/2022

Uploaded on 09/27/2022

schiavi
schiavi 🇬🇧

4.9

(7)

213 documents

1 / 11

Toggle sidebar

This page cannot be seen from the preview

Don't miss anything!

bg1
CS108, Stanford Handout #14
Fall, 2008-09 Osvaldo Jiménez
OOP Inheritance 2
Thanks to Nick Parlante for much of this handout
Here we look at a variety of more advanced inheritance issues.
Is-a vs. Has-a
Specifying a superclass-subclass relationship establishes a very strong constraint between the two classes.
The subclass is constrained to be an "isa" specialization of the superclass. The subclass is merely a
version of the superclass category. This relationship is so constraining... that's one reason why
subclassing opportunities are rare.
e.g. Boat isa Vehicle, Chicken isa Bird isa Animal
Contrast to the much more common "has-a" relationship where one object has a pointer to another
e.g. Boat has-a passenger, Game has-a Tetris piece, University has-a Student has-a Advisor
Student/Grad Memory Layout
Implementation detail where each class has a single superclass (such as in Java): in memory, the ivars of
the subclass are layered on top of the ivars of the superclass (that's the simplest scheme -- the Java
runtime may arrange things differently for performance reasons, but your Java code will no be able to
know that.)
Result: if you have a pointer to the base address of an instance of the subclass (
Grad
), you can treat it as
if it were a superclass object (
Student
) and it just works since the objects look the same from the
bottom up. Again: you can treat an instance of the subclass as if it were the superclass, and it works ok.
A
Grad
object looks like a
Student
object.
Here we have pointers
Student s1, s2;
The pointer
s1
points to a
Student
while
s2
points to a
Grad
. Note how literally the same
getUnits()
code can execute against both memory structures,
since it just looks at the first 4 bytes for both objects -- that part of both the
Student
and
Grad
object
memory look the same and have the same layout. There is just one copy of the code for
getUnits()
up in the
Student
class, and that one copy is used for
Student
and
Grad
objects. Also, you can see
how it is that it does not make any sense to run
getYOT()
on a
Student
object.
units 10
s1
units 10
yot 2
s2
Grad
Student
Abstract Super Class Strategy
Suppose we have three bank-account type classes,
Fee
Nickle
and
Gambler
that are similar, but differ
in their end-month-charge policy.
Factor their common features up into an abstract
Account
class and make them subclasses
Give Account an abstract method
abstract void endMonthCharge();
. "Abstract" because it has a
prototype but no code. A class with an abstract method is itself abstract. An abstract class cannot be
instantiated with
new
, as it is incomplete (missing a method). Subclasses must provide code for the
abstract method to avoid being
abstract
.
pf3
pf4
pf5
pf8
pf9
pfa

Partial preview of the text

Download Java Abstract Classes, Interfaces, and Comparable Interface Example and more Slides English in PDF only on Docsity!

CS108, Stanford Handout # Fall, 2008-09 Osvaldo Jiménez

OOP Inheritance 2

Thanks to Nick Parlante for much of this handout

Here we look at a variety of more advanced inheritance issues.

Is-a vs. Has-a

  • Specifying a superclass-subclass relationship establishes a very strong constraint between the two classes. The subclass is constrained to be an "isa" specialization of the superclass. The subclass is merely a version of the superclass category. This relationship is so constraining... that's one reason why subclassing opportunities are rare.
  • e.g. Boat isa Vehicle, Chicken isa Bird isa Animal
  • Contrast to the much more common "has-a" relationship where one object has a pointer to another
  • e.g. Boat has-a passenger, Game has-a Tetris piece, University has-a Student has-a Advisor

Student/Grad Memory Layout

  • Implementation detail where each class has a single superclass (such as in Java): in memory, the ivars of the subclass are layered on top of the ivars of the superclass (that's the simplest scheme -- the Java runtime may arrange things differently for performance reasons, but your Java code will no be able to know that.)
  • Result: if you have a pointer to the base address of an instance of the subclass (Grad), you can treat it as if it were a superclass object (Student) and it just works since the objects look the same from the bottom up. Again: you can treat an instance of the subclass as if it were the superclass, and it works ok. A Grad object looks like a Student object.
  • Here we have pointers Student s1, s2; The pointer s1 points to a Student while s2 points to a Grad. Note how literally the same getUnits() code can execute against both memory structures, since it just looks at the first 4 bytes for both objects -- that part of both the Student and Grad object memory look the same and have the same layout. There is just one copy of the code for getUnits() up in the Student class, and that one copy is used for Student and Grad objects. Also, you can see how it is that it does not make any sense to run getYOT() on a Student object.

units 10

s

units 10

s2 yot 2

Grad

Student

Abstract Super Class Strategy

  • Suppose we have three bank-account type classes, Fee Nickle and Gambler that are similar, but differ in their end-month-charge policy.
  • Factor their common features up into an abstract Account class and make them subclasses
  • Give Account an abstract method abstract void endMonthCharge();. "Abstract" because it has a prototype but no code. A class with an abstract method is itself abstract. An abstract class cannot be instantiated with new, as it is incomplete (missing a method). Subclasses must provide code for the abstract method to avoid being abstract.
  • In its endMonth() method, the Account class calls the endMonthCharge() method -- knowing that this will pop-down to do the endMonthCharge() provided by the subclass.
  • The abstract superclass can set out logical methods like endMonthCharge() so that subclasses can override them.

public abstract class Account { /* Applies the end-of-month charge to the account. This is "abstract" so subclasses must override and provide a definition. At run time, this will "pop down" to the subclass definition. */ protected abstract void endMonthCharge();

public void endMonth() { // Pop down to the subclass for their // specific charge policy (abstract method) endMonthCharge();

transactions = 0; } ...

// Fee.java public class Fee extends Account { public void endMonthCharge() { withdraw(5.00); } ...

Abstract Bird Analogy

  • Like the word "bird" in English -- you cannot instantiate something which is exactly a bird, but you can for subclasses like mockingbird or chicken. The word "bird" is like an abstract superclass.

Method Override -- Cannot "Narrow" Params

  • When overriding a method, the parameters must be exactly the same in the subclass method as in the superclass method.
  • Suppose we have a Meal class, and it supports a same(Food food) method (this is analogous to the equals() method in Java).

class Food { boolean same(Food food) { ...

  • Suppose we create a Candy subclass of Food.
  • In Candy, suppose we change the same() argument to take Candy instead of Food
  • That looks reasonable, but it runs into logic problems.

class Candy extends Food { private int sugar;

// incorrect override .. Candy arg more narrow than Food boolean same(Candy candy) { // Treat candy like a Candy object in here return (this.sugar == candy.sugar); }

  • The problem is that there could be some client code (such as eat() below) that takes a Food compile time type argument, but is passed a Candy object -- that's allowed by the substitution rule that a subclass may be used in place that calls for its superclass. In eat(), the prototype seen of same() is

Java Interface

  • Method Prototypes
    • An interface defines a set of method prototypes.
    • Does not provide code for implementation -- just the prototypes.
    • Can also define final constants.
  • Class implements interface
    • A class that implements an interface must implement all the methods in the interface. The compiler enforces this at compile time.
    • A Java class can only have one superclass, but it may implement any number of interfaces.
  • "Responds To"
    • The interface is a "responds to" claim about a set of methods.
    • If a class implements the Foo interface, I know it responds to all the messages in the Foo interface.
    • In this sense, an interface is very similar to a superclass.
    • If an object implements the Foo interface, a pointer to the object may be stored in a Foo variable. (Just like storing a pointer to a Grad object in a Student variable.)
  • Lightweight
    • Interfaces allow multiple classes to respond to a common set of messages, but without introducing much complexity into the language.
    • Interfaces are lightweight compared to superclasses.
  • This is similar to subclassing, however...
    • Good news: A class can only have one superclass, however it can implement any number of interfaces. Interfaces are a simple, lightweight mechanism.
    • Bad news: An interface only gives the message prototypes , no implementation code. The class must implement the method from scratch.
  • vs. Multiple Inheritance
    • C++ multiple inheritance is more capable -- multiple superclasses -- but it introduces a lot of compiler and language complexity, so maybe it is not worth it. Interfaces provide 80% of the benefit for 10% of the complexity.

e.g. Moodable Interface

  • Suppose you are implementing some sort of simulation, and there are all sorts of different objects in the program with different superclasses -- Cats, Dogs, Students, Buildings, ....
  • However, you want to add a "mood ring" feature, where we can query the current color mood out an object.
  • Some classes will support mood and some won't
  • We define the Moodable interface -- any class that wants to support the Mood feature, implements the Moodable interface

// Moodable.java public interface Moodable { public Color getMood(); // interface defines getMood() prototype but no code

}

  • If a class claims to implement the Moodable interface, the compiler will enforce that the class must respond to the getMood(); message.

Student implements Moodable

  • Here is what the Student class might look like, extended to implement the Moodable interface. The class must provide code for all the messages mentioned in the interface, in this case just getMood().
  • A class that implements multiple interfaces separates them with commas after the "implements" keyword.

public class Student implements Moodable {

public Color getMood() { if (getStress()>100) return(Color.red); else return(Color.green); } // rest of Student class stuff as before...

Client Side Moodable

  • Moodable is like an additional superclass of Student.
  • It is possible to store a pointer to a Student in a pointer of type Moodable.
  • The type system essentially wants to enforce the "responds to" rules. It's ok to store a pointer to a Student in a Moodable, since Student responds to getMood().
  • So could say... Student s = new Student(10);

Moodable m = s; // Moodable can point to a Student m.getMood(); // this works m.getStress(); // NO does not compile

Polymorphism -- Array example

  • You could have a Moodable[] array, storing pointers to all sorts of Moodable objects. You could iterate over the array, calling getMood() on all the objects ... not worrying about their specific types.
  • This feature -- that you can call getMood() with confidence that it will pop-down and do the right code depending on the class of the receiver -- is know officially as "polymorphism".

"Comparable" Interface Example

  • Example of Java interface -- the Comparable interface
  • Objects that work with Java's built in-sorting machinery implement the Comparable interface, which defines the one method compareTo()...

public interface Comparable { public int compareTo (Object other); }

  • compareTo(Object other) compares the receiver to other object, returning...
    • negative if receiver is "less" (this same convention is used in C and other languages)
    • 0 if same
    • positive if receiver is "more"
  • Trick -- use subtraction: rcvr - other

Java 5 Comparable

  • In Java 5, Comparable has been made generic, so a class Foo can implement Comparable
  • Then, the prototype is compareTo(Foo) instead of compareTo(Object)
  • (See Ingredient example below)

Collections.sort(List)

  • Collections.sort() -- works on a List (e.g. ArrayList) where the elements implement the Comparable interface. Throws a runtime exception if an element does not implement Comparable during the sort.

"Comparator" Interface

  • For a custom sort not based on the built-in Comparable feature of the elements, any object can implement the Comparator interface which compares two objects.
  • int compare(Object a, Object b) -- returns int, like Comparable
  • If a class does not implement hashCode() then it cannot be a key in a HashMap (it can still be a value). String, Integer, … all implement hashCode(), so they can be used as keys fine.
  • For production quality code, if equals() is overridden, then hashCode() should also be overridden to be consistent with equals().

Ingredient Code

// Ingredient.java import java.util.*;

/**

  • The Ingredient class encapsulates the name of the ingredient
  • and its quantity in grams. Used to demonstrate standard overrides:
  • toString(), equals(), hashCode(), and compareTo().
  • Also demonstrates sorting and Javadoc. */

public class Ingredient implements Comparable { private String name; private int grams;

/**

  • Constructs a new Ingredient.
  • @param name name of ingredient
  • @param grams quantity of ingredient in grams */ public Ingredient(String name, int grams) { this .name = name; this .grams = grams; }

/**

  • Gets the name of the Ingredient.
  • @return name of ingredient */ public String getName() { return name; }

/**

  • Gets the grams of the Ingredient.
  • @return grams value */ public int getGrams() { return grams; }

/**

  • Sets the grams of the Ingredient.
  • @param grams new grams value */ public void setGrams( int grams) { this .grams = grams; }

/**

  • Returns a String form of the Ingredient.
  • Uses the format "name (grams-value grams)"
  • @return string form of ingredient */ @Override public String toString() { return name + " (" + grams + " grams)"; }

/**

  • Compares this ingredient to the given object (standard override).
  • @return true if ingredient has the same value as the given object */ @Override public boolean equals(Object obj) { // Note: our argument must be Object, not Ingredient, // to match the equals() prototype up in the Object class.

// Standard equals() tests... if ( this == obj) return true ; if (!(obj instanceof Ingredient)) return false ;

// Now do deep compare Ingredient other = (Ingredient)obj; return (grams==other.getGrams() && name.equals(other.getName())); }

/**

  • Returns an int hashcode for this ingredient (standard override).
  • @return int hashcode of ingredient / @Override public int hashCode() { // if two objects are deeply the same, their // hash codes must be the same return (grams + name.length()11); // could use name.hashCode() instead of name.length() }

/**

  • Compares the ingredient to the given object for sort order.
  • (This is the standard sorting override, implemented here
  • since we implement the "Comparable" interface.)
  • Orders increasing by name, and for the same name, increasing by grams.
  • This is the Java 5 version, generic for Comparable, so
  • the arg is type Ingredient.
  • @return negative/0/positive to indicate ordering vs. given object */ public int compareTo(Ingredient other) { int strCompare = name.compareTo(other.getName()); if (strCompare != 0) return strCompare; else { if (grams < other.getGrams()) return -1; else if (grams > other.getGrams()) return 1; else return 0; // trick: could just return (grams - other.getGrams()) // could access .grams directly ("sibling" access) } }

/**

  • Old, non-generic compareTo(), where arg is type Object.
  • @return negative/0/positive to indicate ordering vs. given object */ private int compareToOld(Object obj) { Ingredient other = (Ingredient)obj; // cast to sibling int strCompare = name.compareTo(other.getName()); if (strCompare != 0) return strCompare; else { if (grams < other.grams) return -1; else if (grams > other.grams) return 1; else return 0; // note: here we refer to sibling ivars just as .grams // since we are in the same class, although using message // send is a slihgtly better OOP de-coupling style. } }
  • http://java.sun.com/j2se/javadoc/writingdoccomments/
  • This is how all the standard class API docs are generated
  • JavaDoc sections start with two stars /** .... */
  • Explain the exposed interface of the class to a client
  • Eclipse code-complete/hover uses javadoc -- a great way to expose timely info to the client.
  • For good examples, see the String or HashMap Javadoc pages
  • In Eclipse, use Project > Generate Javadoc , and you can just leave all the defaults and generate.
  • HTML markup can be used --

  • Javadoc provides a nice, standard solution to a simple and unglamorous but very important problem -- a standard way to document a class for clients.

Class Overview Javadoc

  • At the top of the class, summarize what the class encapsulates and its operational theory for the client. Imagine that the person coming to the page does not know what the class does -- give them a 2 sentence summary.
  • For a large class, include ... sections showing typical client code.
  • Many classes do not do a good job on the class overview, but it's a quality touch to add.

Method Javadoc

  • First sentence should summarize the whole thing. Javadoc uses the first sentence as a summary in the table of methods.
  • What does it do, what effect is there on the receiver, and what are the roles of the parameters.
  • Use the "s" form verb -- "Adds...", "Computes...", "Returns...". Ok to just start with the verb, leaving out the implicit "This method…" at the start.

@param

  • Mention each parameter by name
  • Will be redundant with the main sentence, but the info is helpful in the HTML form for the client.
  • For a method int search(String target)
    • @param target the string to search for

@return

  • For methods that return something
  • Will also be a bit redundant with the summary sentence.
  • For a method int search(String target)
    • @return index of target string if found, or -

Example Bad Docs -- signum()

  • Here's the javadoc for the signum() method in Integer...
  • "signum(int i) -- Returns the signum function of the specified int value."
  • Wow, it's amazing that this "doc" has a sentence with words, and yet conveys no information.
  • A better version would be dense but meaningful: "returns the -1/0/1 sign of the given int value."

Ingredient Example

  • The previous Ingredient example follows the javadoc style.
  • Running the Eclipse -> Produce Javadoc command, produces javadoc like this…