UML, Abstract Classes and Methods, and Interfaces

The Unified Modeling Language (UML)


UML Examples


The Unified Modeling Language (UML) is a standardized way of specifying, visualizing, and documenting the artifacts of an object-oriented software under development (among other things).

To visualize classes and objects and their relationships in this -- a first course in Java -- we will use some of the features of these diagrams. Although, you should know that UML can do much more than just that.

Indeed, UML 2.2 has 14 types of different diagrams, half of which represent structural information, while the other half represent general types of behavior and interactions. Class diagrams and Object Diagrams are but two of the "structure diagrams" that exist.


14 Different Types of Diagrams

Class Diagrams

In UML, one represents a class with a box that has 3 sections: The top section displays the class name, the middle section displays the instance variables of that class, and the bottom section displays its methods. For example, a UML class diagram for a BankAccount class might look like the following:

As can be seen above, we specify the visibility of class members (any data fields, constructors, or methods) by putting the following notations before each member's name:

+          Public
-          Private
#          Protected
~          Package (default visibility)

Other properties of class members, we can indicate with the format of the text:

underline  static
italic     abstract
all-caps   constants

Note in UML, the return type of a method comes AFTER the parameter list for the method, with the two separated by a colon (":").

UML object diagrams are similarly constructed, except they only show the state of the object as determined by its data fields. For example, two objects of type BankAccount are shown below in UML style:


We use (open triangle tipped) arrows to denote subclass relationships. In UML, the arrow points from the subclass to its related superclass.

So for example, if we wanted to describe the class GeometricObject, and two of its subclasses, Circle and Rectangle, we might see something l like the following in UML:

Abstract Classes and Abstract Methods

Notice, in the previously described GeometricObject class, we can't actually implement a getArea() or getPerimeter() method for a GeometricObject in ANY meaningful way, as those methods depend upon what type of shape is involved (circle, rectangle, etc...).

Instead, the Circle and Rectangle subclasses need to override these methods with a meaningful implementation.

So that we don't have to write an implementation for these methods in the GeometricObject class (where they wouldn't make sense, anyways), we declare them as abstract methods in this class. Think of abstract methods as "placeholders" for methods that will eventually be defined in some subclass of the current class.

In doing this, we acknowledge that the Geometric object is not complete in a certain sense -- and it would be inappropriate to instantiate an object of this class. More generally, classes with abstract methods must themselves be declared abstract. That is to say, an abstract method can't be contained in a non-abstract class.

Likewise, if a subclass of an abstract superclass does not implement all the abstract methods of that superclass, the subclass itself must be declared to be abstract. So if one wishes to extend an abstract class, all of the methods of the superclass must be implemented, even if they are not used in the subclass.

Not surprisingly, since they are "incomplete" in a certain sense, ABSTRACT CLASSES MAY NOT BE INSTANTIATED using the new operator.

However, despite not being able to be instantiated, one can still create constructors for abstract classes, which will be invoked via "constructor chaining" upon calls to the constructors of the related subclasses.

Conditions (or lack thereof) on Abstract Classes and Abstract Methods

  1. Certainly, any class that contains abstract methods must be abstract, however -- it is allowable to declare an abstract class that contains no abstract methods. Here too, the abstract class is not allowed to be instantiated using the new operator Such classes typically serve as base classes for defining new subclasses.
  2. A subclass can be abstract even if its superclass is concrete. For example, the Object class is concrete, but may have an abstract subclass like GeometricObject.
  3. A subclass may override a method from its superclass to declare it abstract. This is rare, but useful when the implementation of the method in the superclass becomes invalid in the subclass. Of course, this requires the subclass itself to be declared abstract.

Abstract Class as Type

You can't create an instance from an abstract class using the new operator, BUT abstract classes can be used as legal data types. For example, we can create a reference variable to a Geometric object that references a Circle object, as seen below:

//The uncommented line below is legal even though 
//Geometric Object myGeoObject = new GeometricObject(); <--Error
//is not.

GeometricObject myGeoObject = new Circle(10);     //<--OK
 

//..and this is legal too!

GeometricObject[] myGeoObjects = new GeometricObject[10];  
 

//Note: myGeoObjects above is an array that contains 
//10 references to geometric objects. Of course, to fill 
//the array, you will need to call on the constructors of 
//the subclasses...

myGeoObjects[0] = new Circle(10);
myGeoObjects[1] = new Rectangle(3,4);
myGeoObjects[2] = new Rectangle(5,8);
myGeoObjects[3] = new Circle(7);
...

Interfaces

What is an Interface?

An interface is a class-like construct that contains only constants and abstract methods. In many ways, it is similar to an abstract class -- but it instead aims to specify the behavior of a class that implements it. Of course, a class can have many different behaviors associated with it, so a class might implement several interfaces. For example, we might want to ensure that objects of a particular class are comparable, edible, cloneable, etc... and could do this by having the class implement interfaces Comparable, Edible, Cloneable, etc...

For a class to implement an interface it must implement all of the abstract methods specified in that interface.

Like an abstract class, you cannot create an instance from an interface using the new operator, however you can:

  • create an instance from a class that implements an interface
  • use an interface as a data type for a variable, as the result of casting, and so on...

Defining an Interface

The following syntax should be used to define an interface:

public interface InterfaceName {
   constant declarations;
   method signatures;

Here's an example:

public interface Edible {
    //every class that claims to be "edible"
    //must be able to describe how they can be eaten...
    public abstract String howToEat();
}

Omitting Modifiers in Interfaces

All data fields in an interface are automatically public final static (i.e., constants), and all methods in an interface must public abstract. As such, explicitly including these modifiers in the interface definition is not necessary.

Consequently,

public interface T1 {
   public static final int K = 1;
   public abstract void p();
}

is the same as...

public interface T1 {
   int K = 1;
   void p();
}

Once an interface has been defined, you still need to have some class implement it. Again, classes are not limited to implementing just one interface. As an example, consider the following two interfaces:

public interface Predator {
    boolean chasePrey(Prey p);
    void killPrey(Prey p);
}
public interface Prey {
   void fightFleeOrGetEaten();  //actions taken (or not taken) when 
                                //under the threat of a predator
}

and here two classes that implement one or both of the above:

public class Lion implements Predator {

   public boolean chasePrey(Prey p) {
        return (isWorthEating(p) && this.hungry) 
   }

   public void killPrey(Prey p) {
        useClaws(p);
        while ( ! Dead(p) ) 
             bite(p);
   }

   ...

}
public class Frog implements Predator, Prey {

  public boolean chasePrey(Prey p) {
      return (isBug(p) && isOneToungeLengthAway(p))
  }

  public void killPrey(Prey p) {
      useStickyTongue(p);
      swallow(p);
  }

  public void fightFleeOrGetEaten() {
      tryJumpingAway();
      trySwimmingAway();
  }

  ...

}

As can be seen above, classes may implement multiple interfaces. However, one must take care that there are no conflicting elements in those interfaces (e.g., two constants with the same name but different values, or two methods with the same signature, but different return types. Fortunately, such errors are detected by the compiler.

The Comparable Interface

One very useful interface to implement is the "Comparable" interface. This interface is defined in the java.lang package:

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

Many classes (e.g., String and Date) in the Java library implement Comparable to define a natural order for the objects they define. For example, Strings can be compared lexicographically (i.e., which String would "come first" alphabetically) with the String.compareTo() method. Similarly, one can determine which of two dates "comes first" by appealing to the Date.compareTo() method.

Interfaces vs. Abstract Classes

While somewhat similar, there are distinct differences between interfaces and abstract classes. For example:

  • In an interface, the data must be constants; an abstract class can have all types of data.
  • Each method in an interface has only a signature without implementation, while an abstract class can have concrete methods.

The following table displays some of the major differences:

  Variables Constructors Methods
Abstract Class No restrictions Constructors are invoked by subclasses through constructor chaining, even though an abstract class can't be instantiated using the new operator. No restrictions
Interface All variables must be public static final No constructors. An interface cannot be instantiated using the new operator All methods must be public abstract instance methods

Also, all classes share a single root, the Object class, but there is no single root for interfaces.