Inheritance in Java

Steven Zeil

Last modified: Dec 19, 2017
Contents:

1 Class Inheritance

Inheritance among classes in Java works almost identically to C++. The only difference is a rather minor change in syntax. Instead of this C++ syntax, for example,

class NumericValue: public Value {

Java uses the reserved word extends to indicate the same thing:

class NumericValue extends Value {

What is different in Java is not how we do inheritance, but how often.

1.1 Shrubs and Trees


Inheritance in C++

C++ programs use inheritance only rarely and when there is an obvious need for it.

Spreadsheet inheritance in C++:

 


Inheritance in Java

Spreadsheet inheritance in Java:

 

In Java, our spreadsheet program has the same “topical” inheritance , but our other classes are gathered together into the common tree of Java classes.

The Java API makes a great deal more obvious use of inheritance than does C++ std.

1.2 Inheriting from Object

If a class declaration does not explicitly state a superclass, by default it inherits from Object.

What do we get from this?

Some examples of the members we inherit:


What Can You do to Any Object?

We’ll encounter other Object functions later.

2 Dynamic Binding in Java

Dynamic binding is more pervasive in Java, because

2.1 The Animal Example in Java

Same example, this time in Java. Start with the inheritance hierarchy.

public class Animal {
  public String eats() {return "???";}
  public name() {return "Animal";}
}

public class Herbivore extends Animal {
   public String eats() {return "plants";}
   public String name() {return "Herbivore";}
}

public class Ruminants extends Herbivore {
   public String eats() {return "grass";}
   public String name() {return "Ruminant";}
}

public class Carnivore extends Animal {
   public String eats() {return "meat";}
   public String name() {return "Carnivore";}
}


The Main Program

public class AnimalTest {

  private static void show (String s1, String s2) {
     System.out.println (s1 + " " + s2);
  }

  public static void main (String[]) {
     Animal a = new Animal();
     Herbivore h = new Herbivore();
     Ruminant r = new Ruminant();

     Animal paa = a;
     Animal pah = h;
     Animal par = r;
     
     show(a.name(), a.eats());      // AHRC ?pgm
     show(paa.name(), paa.eats()); // AHRC ?pgm
     show(h.name(), h.eats);        // AHRC ?pgm
     show(pah.name(), pah.eats()); // AHRC ?pgm
     show(par.name(), par.eats()); //AHRC ?pgm
  }
}

Note the absence of the * and & operators.


Animal Hierarchy: a

animal1.java
public class AnimalTest {

  private static void show (String s1, String s2) {
     System.out.println (s1 + " " + s2);
  }

  public static void main (String[]) {
     Animal a = new Animal();
     Herbivore h = new Herbivore();
     Ruminant r = new Ruminant();

     Animal paa = a;
     Animal pah = h;
     Animal par = r;
     
     show(a.name(), a.eats());      // AHRC ?pgm
     show(paa.name(), paa.eats()); // AHRC ?pgm
     show(h.name(), h.eats);        // AHRC ?pgm
     show(pah.name(), pah.eats()); // AHRC ?pgm
     show(par.name(), par.eats()); //AHRC ?pgm
  }
}

Unlike C++, Java resolves all function calls by dynamic binding. And, unlike C++, a is a pointer, whether we like it or not. So, at runtime, the program follows a out to the heap, discovers that it actually points to an Animal, and invokes the Animal::name() function body to print “Animal”. Then it does the same for eats() and invokes the Animal::eats() function body to print “???”.


Animal Hierarchy: paa

animal2.java
public class AnimalTest {

  private static void show (String s1, String s2) {
     System.out.println (s1 + " " + s2);
  }

  public static void main (String[]) {
     Animal a = new Animal();
     Herbivore h = new Herbivore();
     Ruminant r = new Ruminant();

     Animal paa = a;
     Animal pah = h;
     Animal par = r;
     
     show(a.name(), a.eats());      // Animal ???
     show(paa.name(), paa.eats()); // AHRC ?pgm
     show(h.name(), h.eats);        // AHRC ?pgm
     show(pah.name(), pah.eats()); // AHRC ?pgm
     show(par.name(), par.eats()); //AHRC ?pgm
  }
}

paa is treated just like a because, after all, they are both pointers and pointing to the same object.


Animal Hierarchy: h

animal3.java
public class AnimalTest {

  private static void show (String s1, String s2) {
     System.out.println (s1 + " " + s2);
  }

  public static void main (String[]) {
     Animal a = new Animal();
     Herbivore h = new Herbivore();
     Ruminant r = new Ruminant();

     Animal paa = a;
     Animal pah = h;
     Animal par = r;
     
     show(a.name(), a.eats());      // Animal ???
     show(paa.name(), paa.eats()); // Animal ???
     show(h.name(), h.eats);        // AHRC ?pgm
     show(pah.name(), pah.eats()); // AHRC ?pgm
     show(par.name(), par.eats()); //AHRC ?pgm
  }
}

At runtime, the program follows h out to the heap, discovers that it actually points to an Herbivore, and invokes the Herbivore::name() function body to print “Herbivore”. Then it does the same for eats() and invokes the Herbivore::eats() function body to print “plants”.


Animal Hierarchy: pah

animal4.java
public class AnimalTest {

  private static void show (String s1, String s2) {
     System.out.println (s1 + " " + s2);
  }

  public static void main (String[]) {
     Animal a = new Animal();
     Herbivore h = new Herbivore();
     Ruminant r = new Ruminant();

     Animal paa = a;
     Animal pah = h;
     Animal par = r;
     
     show(a.name(), a.eats());      // Animal ???
     show(paa.name(), paa.eats()); // Animal ???
     show(h.name(), h.eats);        // Herbivore plants
     show(pah.name(), pah.eats()); // AHRC ?pgm
     show(par.name(), par.eats()); //AHRC ?pgm
  }
}

At runtime, the program follows pah out to the heap, discovers that it actually points to an Herbivore, and invokes the Herbivore::name() function body to print “Herbivore”. Then it does the same for eats() and invokes the Herbivore::eats() function body to print “plants”.


Animal Hierarchy: par

animal5.java
public class AnimalTest {

  private static void show (String s1, String s2) {
     System.out.println (s1 + " " + s2);
  }

  public static void main (String[]) {
     Animal a = new Animal();
     Herbivore h = new Herbivore();
     Ruminant r = new Ruminant();

     Animal paa = a;
     Animal pah = h;
     Animal par = r;
     
     show(a.name(), a.eats());      // Animal ???
     show(paa.name(), paa.eats()); // Animal ???
     show(h.name(), h.eats);        // Herbivore plants
     show(pah.name(), pah.eats()); // Herbivore plants
     show(par.name(), par.eats()); //AHRC ?pgm
  }
}

At runtime, the program follows par out to the heap, discovers that it actually points to a Ruminant, and invokes the Ruminant::name() function body to print “Ruminant”. Then it does the same for eats() and invokes the Ruminant::eats() function body to print “grass”.


Final Results

animal6.java
public class AnimalTest {

  private static void show (String s1, String s2) {
     System.out.println (s1 + " " + s2);
  }

  public static void main (String[]) {
     Animal a = new Animal();
     Herbivore h = new Herbivore();
     Ruminant r = new Ruminant();

     Animal paa = a;
     Animal pah = h;
     Animal par = r;
     
     show(a.name(), a.eats());      // Animal ???
     show(paa.name(), paa.eats()); // Animal ???
     show(h.name(), h.eats);        // Herbivore plants
     show(pah.name(), pah.eats()); // Herbivore plants
     show(par.name(), par.eats()); // Ruminant grass
  }
}

The overall output here is slightly different from that in the earlier C++ example because, in Java, the name() function is “virtual” and so is handled by dynamic binding.

2.2 The Key Pattern of OOP in Java

Just as in C++, suppose we have an inheritance hierarchy:

 

and that we have a collection of (references to) the BaseClass

 Collection collection;

Then this code:

BaseClass x;
for (each x in collection) {
   x.memberFunction(...);
}

uses dynamic binding to apply subclass-appropriate behavior to each element of a collection.

2.2.1 Examples of the key pattern

There are lots of variations on this pattern. We can use almost any data structure for the collection.


Example: arrays of Animals in Java

 Animal[] animals = new Animal[numberOfAnimals];
    ⋮
 for (int i = 0; i < numberOfAnimals; ++i)
    System.out.println (animals[i].name() 
        + " " + animals[i].eats());

Although the above loop form is familiar, it can be simplified:

 Animal[] animals = new Animal[numberOfAnimals];
    ⋮
 for (Animal a: animals)
    System.out.println (a.name() + " " + a.eats());


Example: Linked Lists of Animals (Java)

 class ListNode {
    Animal data;
    ListNode next;
 }
 ListNode head; // start of list
    ⋮
 for (ListNode current = head; current != null; 
        current = current.next)
    System.out.println (current.name() 
         + " " + current.eats());


Example: vector (ArrayList) of Animals

 ArrayList<Animal> animals = new ArrayList<Animal>();
    ⋮
 for (int i = 0; i < animals.size(); ++i) {
    Animal a = animals.get(i);
    System.out.println (a.name() + " " + a.eats());
 }

or

 ArrayList<Animal> animals = new ArrayList<Animal>();
    ⋮
 for (Animal a: animals) {
    System.out.println (a.name() + " " + a.eats());
 }


Older Style Java

Older Java code would leave out the part within the angle brackets, in which case we would have to treat animals as a container of Objects, not Animals.

downcast.java
 ArrayList animals = new ArrayList();
    //...
 for (Object obja: animals) {
     Animal a = (Animal)obja;
    System.out.println (a.name() + " " + a.eats());
 }

The downcast is required because the loop variable, obja, is of type Object and therefore does not support the name() and eats() functions.

2.3 Abstract Classes in Java

Abstract classes work much the same in Java as in C++. The only differences are


Abstract Class Example

Here, for example, is the Java version of our abstract spreadsheet value class.

Value.java
package SpreadSheetJ.Model;


//
// Represents a value that might be obtained for some spreadsheet cell
// when its formula was evaluated.
// 
// Values may come in many forms. At the very least, we can expect that
// our spreadsheet will support numeric and string values, and will
// probably need an "error" or "invalid" value type as well. Later we may 
// want to add addiitonal value kinds, such as currency or dates.
//
public abstract
class Value implements Cloneable
{
    public abstract String valueKind();
    // Indicates what kind of value this is. For any two values, v1 and v2,
    // v1.valueKind() == v2.valueKind() if and only if they are of the
    // same kind (e.g., two numeric values). The actual character string
    // pointed to by valueKind() may be anything, but should be set to
    // something descriptive as an aid in identification and debugging.
    
    
    public abstract String render (int maxWidth);
    // Produce a string denoting this value such that the
    // string's length() <= maxWidth (assuming maxWidth > 0)
    // If maxWidth==0, then the output string may be arbitrarily long.
    // This function is intended to supply the text for display in the
    // cells of a spreadsheet.

    public String toString()
    {
	return render(0);
    }
    
    public boolean equals (Object value)
    {
	Value v = (Value)value;
	return (valueKind() == v.valueKind()) && isEqual(v);
    }
	

    abstract boolean isEqual (Value v);
    //pre: valueKind() == v.valueKind()
    //  Returns true iff this value is equal to v, using a comparison
    //  appropriate to the kind of value.

}

3 Interfaces In Java

Java offers an alternate, closely related, mechanism for relating classes to one another, the interface.


Example: AudioClip

AudioClip.java
public interface AudioClip { 
    /** 
     * Starts playing this audio clip. Each time this method is called,  
     * the clip is restarted from the beginning.  
     */ 
    void play(); 
 
    /** 
     * Starts playing this audio clip in a loop.  
     */ 
    void loop(); 
 
    /** 
     * Stops playing this audio clip.  
     */ 
    void stop(); 
} 

Example: Cloneable

Signals that a class has a working clone() function.

3.1 Interface Implementation is Not Inheritance


So What is it, Then?

A class that implements an interface can be used wherever that interface is “expected”. That’s pretty much our definition of subtyping.

3.2 Example: Sorting in Java

Suppose we wanted to provide a class that collected a number of useful sorting algorithms.

How do we tell potential users of our sorting routines how to provide comparison functions that we can use?


A C++ style Solution - Inheritance

abstract class Comparable {
  public abstract boolean comesBefore (Object o);
}

One solution is to define the “comparable” protocol as a class.

Then we can write our sorting functions to work on objects of this Comparable class (which we really never expect to ever see) or of any subtype of Comparable:


Calling on a Comparable Class

class Sorting {

   public static void
     insertionSort (Comparable[] array)
   {
      for (int i = 1; i < array.length; ++i) {
        Comparable temp = array[i];
        int p = i;
        while ((i > 0)
          && temp.comesBefore(array[p-1])) {
           array[p] = array[p-1];
           p--;
        }
        array[p] = temp;
     }
   }
     ⋮
 }


Extending Comparable

Here’s an example of how we might declare a class that overrides comesBefore to provide a “sensible” implementation we can use for sorting.

 class Student extends Comparable
 {
   String name;
   String id;
   double gpa;
   String school;
 
   boolean comesBefore(Object o)
   {
     return gpa > ((Student)o).gpa;
   }
 
 }

In this case, we can sort students by grade point average.


A Closer Look

The downcast in our application

studentsort1.java
 class Student extends Comparable
 {
   String name;
   String id;
   double gpa;
   String school;
 
   boolean comesBefore(Object o)
   {
     return gpa > ((Student)o).gpa;
   }
 
 }

is ugly (but required) to match:

abstract class Comparable {
  public abstract boolean comesBefore (Object o);
}

Making Comparable Generic

We can get rid of it by making Comparable generic, so that it knows what kind of objects it is actually comparing:

abstract class Comparable<T extends Object> {
  public abstract boolean comesBefore (T t);
}

and then letting the Student class pass that info along:

 class Student extends Comparable<Student>
 {
   String name;
   String id;
   double gpa;
   String school;
 
   boolean comesBefore(Student s)
   {
     return gpa > s.gpa;
   }
 
 }


A Lurking Problem

What if Student is already inheriting from another class?

 class Person
 {
   String name;
   String id;
 }
 
 class Student extends Person
 {
   double gpa;
   String school;
 }


Why is This a Problem?

That would be no problem in C++, which permits multiple inheritance:

class Student: public Person, Comparable {

But Java only allows a class to have a single superclass, so we can’t add extends Comparable


A Java style Solution - Interface

Java programmers are more likely to approach this problem using an interface:

 package java.lang;
 
 public interface Comparable<T extends Object> {
   /**
       Compares this object with the specified object for order.
       Returns a negative integer, zero, or a positive integer
       as this object is less than, equal to, or greater than
       the specified object.
   */
   int compareTo (T other);
 }


Calling on a Comparable Interface

We use interfaces in our sorting routine just like we would use any kind of data type:

  public static void
     insertionSort (Comparable[] array)
   {
     for (int i = 1; i < array.length; ++i) {
        Comparable temp = array[i];
        int p = i;
        while ((p > 0)
          && temp.compareTo(array[p-1]) < 0) {
           array[p] = array[p-1];
           p--;
        }
        array[p] = temp;
     }
   }


Implementing the Comparable Interface

But now our student class does not have to inherit

comparableStudents.java
 class Student extends Person
               implements Comparable<Student>
 {
   double gpa;
   String school;
 
   int compareTo(Object o)
   {
     Student s = (Student)o;
     if (gpa < s.gpa)
       return -1;
     else if (gpa == s.gpa)
       return 0;
     else
       return 1;
   }
}

This was Just an Example

 package java.util;
 
 public class Arrays
 {
    ⋮
    public static void sort (Comparable[]);
    public static int binarySearch 
        (Comparable[], Comparable key);

3.3 Iterators in Java

java.util.Iterator

Iterator.java
/*
 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package java.util;

/**
 * An iterator over a collection.  {@code Iterator} takes the place of
 * {@link Enumeration} in the Java Collections Framework.  Iterators
 * differ from enumerations in two ways:
 *
 * <ul>
 *      <li> Iterators allow the caller to remove elements from the
 *           underlying collection during the iteration with well-defined
 *           semantics.
 *      <li> Method names have been improved.
 * </ul>
 *
 * <p>This interface is a member of the
 * <a href="{@docRoot}/../technotes/guides/collections/index.html">
 * Java Collections Framework</a>.
 *
 * @param <E> the type of elements returned by this iterator
 *
 * @author  Josh Bloch
 * @see Collection
 * @see ListIterator
 * @see Iterable
 * @since 1.2
 */
public interface Iterator<E> {
    /**
     * Returns {@code true} if the iteration has more elements.
     * (In other words, returns {@code true} if {@link #next} would
     * return an element rather than throwing an exception.)
     *
     * @return {@code true} if the iteration has more elements
     */
    boolean hasNext();

    /**
     * Returns the next element in the iteration.
     *
     * @return the next element in the iteration
     * @throws NoSuchElementException if the iteration has no more elements
     */
    E next();

    /**
     * Removes from the underlying collection the last element returned
     * by this iterator (optional operation).  This method can be called
     * only once per call to {@link #next}.  The behavior of an iterator
     * is unspecified if the underlying collection is modified while the
     * iteration is in progress in any way other than by calling this
     * method.
     *
     * @throws UnsupportedOperationException if the {@code remove}
     *         operation is not supported by this iterator
     *
     * @throws IllegalStateException if the {@code next} method has not
     *         yet been called, or the {@code remove} method has already
     *         been called after the last call to the {@code next}
     *         method
     */
    void remove();
}

The key operations are:


Example: Using an Iterator

 LinkedList<Book> books = new LinkedList<Book>();
 books.add(cs330Text);
 books.add(cs361Text);
 books.add(cs252Text);
   ⋮
 boolean found = false;
 Iterator<Book> it = books.iterator();
 while (it.hasNext() && !found)
 {
   Book b = it.next();
   found = b.equals(cs252Text);
 }
 if (found)
 {
   it.remove();
 }


Not as Strange as it Looks

Compare that to the C++ equivalent:

 list<Book> books;
 books.push_back(cs330Text);
 books.push_back(cs361Text);
 books.push_back(cs252Text);
   ⋮
 bool found = false;
 list<Book>::iterator it = books.begin();
 while (it != books.end() && !found)
 {
   found = (*it == cs252Text);
   ++it;
 }
 if (found)
 {
   books.erase (it);
 }


Iterators: java and C++

Some rough correspondences:

Java C++
Iterator<E> container<E>::iterator
container.iterator() container.begin()
it.hasNext() it != container.end()
x = it.next(); x = *it; it++;
it.remove(); container.erase(it);

Limitations of the Java Iterator

not useful as positions where something was found when searching. In C++ we might write:

list<Book>::iterator find (list<Book>& books, 
                           string title)
{
  for (list<Book>::iterator it = books.begin();
         it != books.end(); ++it)
    {
     if (it->getTitle() == title)
        return it;
    }
  return books.end();
}

*  There's no equivalent to that using Java iterators. 

Limitations of the Java Iterator (cont.)

Similarly, there’s no easy equivalent in Java to the C++ practice of using iterators as starting and ending positions of an operation:

template <typename Iterator>
Iterator copy (Iterator start, Iterator stop, 
               Iterator dest)
{
   while (start != stop)
     {
       *dest = *start;
       ++start; ++dest;
     }
}


Iterators and the “for each” Loop

For any container type C that has a function iterator() that returns a value of type Iterator<T>, we can rewrite

 C c = <: ... :>
   ⋮
 for (Iterator<T> it = c.iterator();
         it.hasNext(); ) {
   T t = it.next();
     ⋮
 }

by

 C c = <: ... :>
   ⋮
 for (T t: c) {
     ⋮
 }


Extending Interfaces

Interfaces can extend (inherit from) other interfaces.

ListIterator.java
/*
 * Copyright (c) 1997, 2007, Oracle and/or its affiliates. All rights reserved.
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * This code is free software; you can redistribute it and/or modify it
 * under the terms of the GNU General Public License version 2 only, as
 * published by the Free Software Foundation.  Oracle designates this
 * particular file as subject to the "Classpath" exception as provided
 * by Oracle in the LICENSE file that accompanied this code.
 *
 * This code is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 * version 2 for more details (a copy is included in the LICENSE file that
 * accompanied this code).
 *
 * You should have received a copy of the GNU General Public License version
 * 2 along with this work; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
 *
 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
 * or visit www.oracle.com if you need additional information or have any
 * questions.
 */

package java.util;

/**
 * An iterator for lists that allows the programmer
 * to traverse the list in either direction, modify
 * the list during iteration, and obtain the iterator's
 * current position in the list. A {@code ListIterator}
 * has no current element; its <I>cursor position</I> always
 * lies between the element that would be returned by a call
 * to {@code previous()} and the element that would be
 * returned by a call to {@code next()}.
 * An iterator for a list of length {@code n} has {@code n+1} possible
 * cursor positions, as illustrated by the carets ({@code ^}) below:
 * <PRE>
 *                      Element(0)   Element(1)   Element(2)   ... Element(n-1)
 * cursor positions:  ^            ^            ^            ^                  ^
 * </PRE>
 * Note that the {@link #remove} and {@link #set(Object)} methods are
 * <i>not</i> defined in terms of the cursor position;  they are defined to
 * operate on the last element returned by a call to {@link #next} or
 * {@link #previous()}.
 *
 * <p>This interface is a member of the
 * <a href="{@docRoot}/../technotes/guides/collections/index.html">
 * Java Collections Framework</a>.
 *
 * @author  Josh Bloch
 * @see Collection
 * @see List
 * @see Iterator
 * @see Enumeration
 * @see List#listIterator()
 * @since   1.2
 */
public interface ListIterator<E> extends Iterator<E> {
    // Query Operations

    /**
     * Returns {@code true} if this list iterator has more elements when
     * traversing the list in the forward direction. (In other words,
     * returns {@code true} if {@link #next} would return an element rather
     * than throwing an exception.)
     *
     * @return {@code true} if the list iterator has more elements when
     *         traversing the list in the forward direction
     */
    boolean hasNext();

    /**
     * Returns the next element in the list and advances the cursor position.
     * This method may be called repeatedly to iterate through the list,
     * or intermixed with calls to {@link #previous} to go back and forth.
     * (Note that alternating calls to {@code next} and {@code previous}
     * will return the same element repeatedly.)
     *
     * @return the next element in the list
     * @throws NoSuchElementException if the iteration has no next element
     */
    E next();

    /**
     * Returns {@code true} if this list iterator has more elements when
     * traversing the list in the reverse direction.  (In other words,
     * returns {@code true} if {@link #previous} would return an element
     * rather than throwing an exception.)
     *
     * @return {@code true} if the list iterator has more elements when
     *         traversing the list in the reverse direction
     */
    boolean hasPrevious();

    /**
     * Returns the previous element in the list and moves the cursor
     * position backwards.  This method may be called repeatedly to
     * iterate through the list backwards, or intermixed with calls to
     * {@link #next} to go back and forth.  (Note that alternating calls
     * to {@code next} and {@code previous} will return the same
     * element repeatedly.)
     *
     * @return the previous element in the list
     * @throws NoSuchElementException if the iteration has no previous
     *         element
     */
    E previous();

    /**
     * Returns the index of the element that would be returned by a
     * subsequent call to {@link #next}. (Returns list size if the list
     * iterator is at the end of the list.)
     *
     * @return the index of the element that would be returned by a
     *         subsequent call to {@code next}, or list size if the list
     *         iterator is at the end of the list
     */
    int nextIndex();

    /**
     * Returns the index of the element that would be returned by a
     * subsequent call to {@link #previous}. (Returns -1 if the list
     * iterator is at the beginning of the list.)
     *
     * @return the index of the element that would be returned by a
     *         subsequent call to {@code previous}, or -1 if the list
     *         iterator is at the beginning of the list
     */
    int previousIndex();


    // Modification Operations

    /**
     * Removes from the list the last element that was returned by {@link
     * #next} or {@link #previous} (optional operation).  This call can
     * only be made once per call to {@code next} or {@code previous}.
     * It can be made only if {@link #add} has not been
     * called after the last call to {@code next} or {@code previous}.
     *
     * @throws UnsupportedOperationException if the {@code remove}
     *         operation is not supported by this list iterator
     * @throws IllegalStateException if neither {@code next} nor
     *         {@code previous} have been called, or {@code remove} or
     *         {@code add} have been called after the last call to
     *         {@code next} or {@code previous}
     */
    void remove();

    /**
     * Replaces the last element returned by {@link #next} or
     * {@link #previous} with the specified element (optional operation).
     * This call can be made only if neither {@link #remove} nor {@link
     * #add} have been called after the last call to {@code next} or
     * {@code previous}.
     *
     * @param e the element with which to replace the last element returned by
     *          {@code next} or {@code previous}
     * @throws UnsupportedOperationException if the {@code set} operation
     *         is not supported by this list iterator
     * @throws ClassCastException if the class of the specified element
     *         prevents it from being added to this list
     * @throws IllegalArgumentException if some aspect of the specified
     *         element prevents it from being added to this list
     * @throws IllegalStateException if neither {@code next} nor
     *         {@code previous} have been called, or {@code remove} or
     *         {@code add} have been called after the last call to
     *         {@code next} or {@code previous}
     */
    void set(E e);

    /**
     * Inserts the specified element into the list (optional operation).
     * The element is inserted immediately before the element that
     * would be returned by {@link #next}, if any, and after the element
     * that would be returned by {@link #previous}, if any.  (If the
     * list contains no elements, the new element becomes the sole element
     * on the list.)  The new element is inserted before the implicit
     * cursor: a subsequent call to {@code next} would be unaffected, and a
     * subsequent call to {@code previous} would return the new element.
     * (This call increases by one the value that would be returned by a
     * call to {@code nextIndex} or {@code previousIndex}.)
     *
     * @param e the element to insert
     * @throws UnsupportedOperationException if the {@code add} method is
     *         not supported by this list iterator
     * @throws ClassCastException if the class of the specified element
     *         prevents it from being added to this list
     * @throws IllegalArgumentException if some aspect of this element
     *         prevents it from being added to this list
     */
    void add(E e);
}
addViaIterator.java
 LinkedList<Book> books = new LinkedList<Book>();
 books.add(cs330Text);
 books.add(cs361Text);
 books.add(cs252Text);
   <:\smvdots:>
 boolean found = false;
 ListIterator<Book> it = books.listIterator();
 while (it.hasNext() && !found)
 {
   Book b = (Book)it.next();
   found = (b.equals(cs252Text);
 }
 if (found)
 {
   it.remove();
 }
 it.add(cs252textVersion2);