JMU
An Introduction to Specialization/Generalization and Inheritance
With Examples in Java


Prof. David Bernstein
James Madison University

Computer Science Department
bernstdh@jmu.edu


Motivation
Review
Review (cont.)
Subsets and Supersets
Specialization/Generalization of Classes
Specialization of enums
Inheritance
Specialization/Generalization in UML

images/UML-specialization.gif

Class Hierarchies

An Example

images/specialization_vehicle.gif

The Syntax of Specialization in Java
Specialization and Constructors in Java
Common Uses of Specialization
Adding Characteristics to an Existing Class
Adding Characteristics to an Existing Class (cont.)

An Example

javaexamples/oopbasics/FieldFormat.java
import java.text.*;

/**
 * A DecimalFormat that understand the notion of a "field".
 * That is, you can set the field width of a FieldFormat and
 * it will right-justify the formatted String in a field
 * of that width.
 *
 * Note: This version is for illustrative purposes only.
 *       It does not override all constructors.
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class FieldFormat extends DecimalFormat
{
    private int        fieldWidth = -1;

    /**
     * Default Constructor
     */
    public FieldFormat()
    {
        super();
    }

    /**
     * Change the length of a String to fit exactly into
     * a field with the current width
     *
     * If the String is lengthened it will be right-justified
     *
     * @param original  The original String
     * @return          The shortened or lengthened String
     */
    public String fitStringToField(String original)
    {
        int            i;
        String         returnString;

        returnString = "";

        if (fieldWidth > 0)  // The field width has been set
        {
           if (original.length() <= fieldWidth)  // Lengthen original
           {
              for (i=1; i <= fieldWidth-original.length(); i++)
              {
                 returnString += " ";
              }

              returnString += original;
            } 
           else                                // Shorten original
           {
              returnString = original.substring(0, fieldWidth);
           }
        }
        else              // The field width hasn't been set
        {
            returnString = original;
        }

        return returnString;
    }

    /**
     * Set the field width
     *
     * @param width  The width of the field
     */
    public void setFieldWidth(int width)
    {
        fieldWidth = width;
    }
}
        
Will It Compile and Why/Why Not?
images/FieldFormat.gif
javaexamples/oopbasics/FieldFormatWhatHappensAndWhy.java
import java.text.*;

/**
 * A series of questions involving the 
 * FieldFormat class
 *
 * Note:  Before testing question n, comment-out questions 
 *        before and after.  Each question stands alone.
 */
public class FieldFormatWhatHappensAndWhy
{
    /**
     * The entry point
     *
     * @param args   The command line arguments
     */
    public static void main(String[] args)
    {
        DecimalFormat         decimal;
        FieldFormat           field;
        String                output, temp;


        // Question 1
        decimal = new DecimalFormat();
        decimal.setFieldWidth(5);
        output = decimal.format(12.5);
        System.out.println(output);

        
        // Question 2
        field = new FieldFormat();
        field.setMinimumIntegerDigits(3);
        output = field.format(12.5);
        System.out.println(output);


        // Question 3
        decimal = new DecimalFormat();
        decimal.setMinimumIntegerDigits(3);
        field = decimal;
        output = field.format(12.5);
        System.out.println(output);


        // Question 4
        field = new FieldFormat();
        field.setMinimumIntegerDigits(3);
        decimal = field;
        output = decimal.format(12.5);
        System.out.println(output);


        // Question 5
        field = new FieldFormat();
        field.setMinimumIntegerDigits(3);
        field.setFieldWidth(10);
        decimal = field;
        output = decimal.format(12.5);
        System.out.println(output);


        // Question 6
        field = new FieldFormat();
        field.setMinimumIntegerDigits(3);
        field.setFieldWidth(10);
        decimal = field;
        output = decimal.fitStringToField("12.5");
        System.out.println(output);

        // Question 7
        field = new FieldFormat();
        field.setFieldWidth(20);
        temp   = field.format(12.5);
        output = field.fitStringToField(temp);
        System.out.println(output);

        // Question 8
        decimal = new DecimalFormat();
        setupField(decimal);
        output = decimal.format(12.5);
        System.out.println(output);

        // Question 9
        decimal = new DecimalFormat();
        setupFormat(decimal);
        output = decimal.format(12.5);
        System.out.println(output);

        // Question 10
        field = new FieldFormat();
        setupField(field);
        output = field.fitStringToField(temp);
        System.out.println(output);
    }



    /**
     * Setup a format object
     *
     * @param formatter   The format object
     */
    private static void setupField(FieldFormat  formatter)
    {
        setupFormat(formatter);
        
        formatter.setFieldWidth(20);
    }


    /**
     * Setup a format object
     *
     * @param formatter   The format object
     */
    private static void setupFormat(DecimalFormat  formatter)
    {
        formatter.setMinimumFractionDigits(2);
        formatter.setMaximumFractionDigits(2);
    }
}
        
Modifying Behavior of an Existing Class
Modifying Behavior of an Existing Class (cont.)

An Example

javaexamples/oopbasics/CaseInsensitiveStringTokenizer.java
import java.util.*;

/**
 * A specialized version of the StringTokenizer that ignores the
 * case of the delimiters.  That is, if either 'a' or 'A' is
 * made a delimiter then both 'a' and 'A' will be treated as
 * delimiters.
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class CaseInsensitiveStringTokenizer extends StringTokenizer
{
    /**
     * Explicit Value Constructor
     *
     * @param str  The String to tokenize
     *
     * @see java.util.StringTokenizer#StringTokenizer(java.lang.String)
     */
    public CaseInsensitiveStringTokenizer(String str)
    {
        super(str);
    }

    /**
     * Explicit Value Constructor
     *
     * @param str          The String to tokenize
     * @param delim        The set of delimiters to use
     *
     * @see java.util.StringTokenizer#StringTokenizer(java.lang.String,java.lang.String)
     */
    public CaseInsensitiveStringTokenizer(String str, String delim)
    {
        this(str, delim, false);
    }

    /**
     * Explicit Value Constructor
     *
     * @param str          The String to tokenize
     * @param delim        The set of delimiters to use
     * @param returnDelims true to return delimiters as tokens
     *
     * @see java.util.StringTokenizer#StringTokenizer(java.lang.String,java.lang.String)
     */
    public CaseInsensitiveStringTokenizer(String str, String delim, 
                                          boolean returnDelims)
    {
        // While admittedly messy, remember that the call to super() 
        // must be in the first statement in the method
        super(str, delim.toLowerCase()+delim.toUpperCase(), returnDelims);
    }
}
        
Will It Compile and Why/Why Not?
images/CaseInsensitiveStringTokenizer.gif
javaexamples/oopbasics/CaseInsensitiveStringTokenizerWhatHappensAndWhy.java
import java.util.*;

/**
 * A series of questions involving the 
 * CaseInensitiveStringTokenizer class
 *
 * Note:  Before testing question n, comment-out questions 
 *        before and after.  Each question stands alone.
 */
public class CaseInsensitiveStringTokenizerWhatHappensAndWhy
{
    /**
     * The entry point
     *
     * @param args   The command line arguments
     */
    public static void main(String[] args)
    {
        CaseInsensitiveStringTokenizer     insensitive;
        String                             delimiters, test, token;
        StringTokenizer                    sensitive;


        test = "ThisXisxaXsimplextestXIxthink";
        delimiters = "x";

        // Question 1
        sensitive = new StringTokenizer(test, delimiters);
        printTokens(sensitive);


        // Question 2
        insensitive = new CaseInsensitiveStringTokenizer(test, delimiters);
        printTokens(insensitive);

    }



    /**
     * Loop over all tokens and print them
     *
     * @param   tokenizer   The StringTokenizer to use
     */
    private static void printTokens(StringTokenizer tokenizer)
    {
        String        token;



        System.out.println("Tokens: ");

        while (tokenizer.hasMoreTokens())
        {
            token = tokenizer.nextToken();
            System.out.println(token);
        }
        System.out.println("\n");
    }

}
        
Adding/Modifying Characteristics "From Scratch"
Adding Characteristics "From Scratch"

An Example

javaexamples/oopbasics/emergency1/EmergencyMessage.java
/**
 * A message containing emergency information
 *
 * @version 1.0
 * @author  Prof. David Bernstein, James Madison University
 */
public class EmergencyMessage
{
    private String message;


    /**
     * Construct a new EmergencyMessage
     *
     * @param message   The text of the mesage
     *
     */
    public EmergencyMessage(String message)
    {
        this.message = message;
    }

    /**
     * Return the text of this message
     *
     * @return   The text of the message
     */
    public String getMessage()
    {
        return message;
    }
}
        
Adding Characteristics "From Scratch" (cont.)

An Example (cont.)

javaexamples/oopbasics/emergency1/Alert.java
/**
 * A message containing normal emergency 
 * information and a supplemental alert
 *
 * @version 1.0
 * @author  Prof. David Bernstein, James Madison University
 */
public class Alert extends EmergencyMessage
{
    private String supplement;


    /**
     * Construct a new Alert
     *
     * @param message      The main mesage
     * @param supplement   The supplemental information
     *
     */
    public Alert(String message, String supplement)
    {
        super(message);
        this.supplement = supplement;
    }

    /**
     * Return the supplemental information
     *
     * @return   The supplemental information
     */
    public String getSupplement()
    {
        return supplement;
    }
}
        
Modifying Behavior "From Scratch"

An Example

javaexamples/oopbasics/im1/Chirp.java
import java.util.*;

/**
 * A missive in an instant messaging system
 *
 * @author  Prof. David Bernstein
 * @version 1.0
 */
public class Chirp
{
    private String          delimiters;  // Delimiters between words
    private String          text;        // The text of the message

    /**
     * Explicit Value Constructor
     *
     * @param typed   What was typed by the user
     */
    public Chirp(String typed)
    {
        text       = typed;
        delimiters = " ,.;!?-\n\r";
    }

    /**
     * Get the number of words in this Chirp
     *
     * @return   The number of words
     */
    public int getNumberOfWords()
    {
        int                 numberOfWords;
        StringTokenizer     tokenizer;

        tokenizer     = new StringTokenizer(text, delimiters);
        numberOfWords = tokenizer.countTokens();

        return numberOfWords;
    }

    /**
     * Get the delimiters
     *
     * @return   The delimiters
     */
    public String getDelimiters()
    {
        return delimiters;
    }

    /**
     * Get the text of this Chirp
     *
     * @return   The text
     */
    public String getText()
    {
        return text;
    }
}
        
Modifying Behavior "From Scratch" (cont.)

An Example (cont.)

javaexamples/oopbasics/im1/ExpandedChirp.java
import java.io.*;
import java.util.*;

/**
 * A missive in an instant messaging system
 *
 * Unlike a normal Chirp, an ExpandedChirp does not
 * contain abbreviations -- they are all expanded
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class ExpandedChirp extends Chirp
{
    private Properties      abbreviations;


    /**
     * Explicit Value Constructor
     *
     * @param typed   What was typed by the user
     */
    public ExpandedChirp(String typed)
    {
        super(typed);
        InputStream      is;

        abbreviations = new Properties();
        try
        {
            is = new FileInputStream("abbreviations.txt");
            abbreviations.load(is);
        }
        catch (IOException ioe)
        {
            // There was a problem opening the file
            // containing the abbreviations
        }
    }

    /**
     * Get the text of this Chirp with all of the
     * abbreviations expanded
     *
     * This method overrides the version in the parent
     *
     * @return   The text
     */
    public String getText()
    {
        String       tempText;

        // Use getText() in the parent
        tempText  = replaceAbbreviations(super.getText());

        return tempText;
    }

    /**
     * Replace the abbreviations in a message
     *
     * @param msg   The original message
     */
    private String replaceAbbreviations(String msg)
    {
        String              replaced, token, word;
        StringTokenizer     tokenizer;

        replaced = "";
        
        tokenizer = new StringTokenizer(msg, getDelimiters(), true);
        while (tokenizer.hasMoreTokens())
        {
            token = tokenizer.nextToken();
            word  = abbreviations.getProperty(token);

            if (word == null) replaced += token;
            else              replaced += word;
        }

        return replaced;
    }
}
        
Modifying Behavior "From Scratch" (cont.)

An Example (cont.)

javaexamples/oopbasics/im1/ChirpDriver.java
/**
 * A driver for illustrating the Chirp and EnglishChirp
 * classes
 *
 * @author  Prof. David Bernstein, James Madison University
 * @version 1.0
 */
public class ChirpDriver
{
    /**
     * The entry point of the application
     *
     * @param args The command line arguments
     */
    public static void main(String[] args) 
    {

        if ((args.length > 0) && (args[0].equals("-old"))) 
        {
            ExpandedChirp     chirp;
            chirp = new ExpandedChirp("This is it.  TTFN!");
            System.out.println(chirp.getText());
        }
        else
        {
            Chirp        chirp;
            chirp = new Chirp("This is it.  TTFN!");
            System.out.println(chirp.getText());
        }
    }
}
        
Shadowing
Conventions when Overriding Methods
One Common Mistake
Another Common Mistake
A Common Mistake (cont.)
        Chirp          c;
        ExpandedChirp  e;
        
        c = new Chirp("TTFN");
        e = (ExpandedChirp)c;
  
Static Members
Preventing Specialization in Java
Preventing Specialization in UML
A Peculiarity of Specialization in Java
A Java Detail we Skipped Earlier
Some Other Details we have "Danced Around"