Abstraction Techniques
Interfaces
An interface is a class-like construct considered to be a reference type. This means that an object may be of the interface type and referenced as such.
A Java interface can include any of:
- Constants. Fields and their values can be specified. The values are treated as constants – their values cannot be changed. The Java term used to describe something that cannot be altered is final.
- Method signatures. Methods are named, their return type and parameter lists are specified.
- Note: Prior to Java 8, these methods could only be abstract. Starting with Java 8, new features like lambda methods required some tweaks to interfaces to allow backwards compatibility with existing interfaces. To allow backwards compatibility, interfaces can now have default methods. However, interfaces are intended to only convey method signatures and leave the implementation up to the implementing class, so the use of default methods should only be used if backwards compatibility is an issue.
An interface thus consists of a set of instance method interfaces, without any associated implementations. A class can implement an interface by providing an implementation for each of the methods specified by the interface. Note that to implement an interface, a class must do more than simply provide an implementation for each method in the interface; it must also state that it implements the interface, using the reserved word implements as in this example: “public class Foo implements BarInterface
“. Any concrete class that implements the BarInterface
interface must provide definitions for each method listed in the interface. We say that an object implements an interface if it belongs to a class that implements the interface.
One of the biggest benefits of using interfaces is their flexibility. A class can implement as many interfaces as it wants, but a class can only extend exactly one class. In fact, a class can both extend one other class and implement one or more interfaces.
The point of all this is that, although interfaces are not classes, they are something very similar. An interface is very much like an abstract class, that is, a class that can never be used for constructing objects, but can be used as a basis for making subclasses. The subroutines in an interface are abstract methods, which must be implemented in any concrete class that implements the interface.
Example: Comparable Interface
Lecture: Comparable Comparators (18 minutes)
One commonly used interface defined by Java is the Comparable Interface. This interface contains a single method, compareTo. This is the same compareTo method that you’ve used before in the String class. Classes that implement Comparable are guaranteed to have a compareTo method, which is useful for implementing generalized search, sort, min, or max methods.
Remember from our discussion on polymorphism that an Interface type can be used as a reference type for an object. Thus, you could have a sorting method that can sort any class that implements Comparable. This is exactly how Arrays.sort and Collections.sort is set up.
Here is the documentation for one of the Collections.sort methods. We’ll discuss this syntax more when we discuss Generic Programming, but this signature says that as long as the elements in the list implement Comparable, you can call this sort method on that list.
// Strings are Comparable ArrayList<String> strList = new ArrayList<String>(); strList.add("zebra"); strList.add("addax"); strList.add("black footed ferret"); strList.add("koala"); Collections.sort(strList); // will print in lexicographical order System.out.println("List of Strings:"); for (String animal : strList) { System.out.println(animal); } // Crates are not Comparable, and so cannot be // sorted ArrayList<Crate> storage = new ArrayList<Crate>(); for (int i = 0; i < strList.size(); i++) { storage.add(new Crate()); } // Add elements in reverse int j = strList.size() - 1; for (int i = 0; i < storage.size(); i++) { storage.get(i).add(strList.get(j)); j--; } // Not allowed! // Collections.sort(storage); System.out.println("List of Crates:"); for (Crate elem : storage) { System.out.println(elem.peek()); }
Note that this interface does not impose any other restrictions whatsoever! As long as the elements implement Comparable and can be compared with each other, you can call sort on your List.
Example: BoxInterface
Let’s look at a custom interface example. The Boxable Interface represents the abstract idea of a box. We can put things in the box, take things out of the box, and look in the box to see what’s in the box.
/** * Defines the basic behavior of a box */ public interface Boxable { /** * Adds an element to the Box * @return true if element successfully added; false otherwise */ public boolean add(String element); /** * Removes an element from the Box */ public void remove(String element); /** * @return reference to item in the box */ public String peek(); }
Here’s a few examples of classes that implement Boxable. A crate is an example of something that probably very closely aligns with your mental image of a box – something hollow in the shape of a square or rectangle with walls.
/** * Represents a shipping crate */ public class Crate implements Boxable { private String contents; // a very small box public Crate() { contents = null; } public boolean add(String element) { if (contents == null) { contents = element; return true; } return false; } public String remove(String element) { String retVal = null; if (contents != null && contents.equals(element)) { retVal = contents; contents = null; } return retVal; } public String peek() { if (contents == null) { return "Empty crate"; } return "Crate contains: " + contents; } }
A suitcase, while not typically what you’d call a box, exhibits the behavior of a box. You can put stuff in, take stuff out, and look in it. This shows how an interface only specifies that an object implements specific behaviors, but otherwise the implementing classes don’t necessarily need to be otherwise related.
/** * Represents a suitcase */ public class Suitcase implements Boxable { private String[] contents; private int numItems; public Suitcase() { // suitcase has 5 compartments contents = new String[5]; numItems = 0; } public boolean add(String element) { if (numItems & lt; contents.length) { contents[numItems] = element; numItems++; return true; } return false; } public String remove(String element) { // Suitcase is empty if (numItems == 0) return null; // Find the element to remove int i = 0; while (!contents[i].equals(element) & amp; & amp; i & lt; numItems) { i++; } // If not found, return null if (i == numItems) return null; // If found, remove element and shift String retVal = contents[i]; while (i & lt; numItems - 1) { contents[i] = contents[i + 1]; i++; } return retVal; } public String peek() { String res = "Suitcase compartments contain: "; for (String elem: contents) { res += elem + " "; } return res; } }
Finally, here’s an example of a box that implements both Boxable and Comparable. This object exhibits the behavior of a box and can be sorted! You could create a list of ShippingBoxes and sort them using Collections.sort.
/** * Represents a box used for Shipping that can be sorted by weight * */ public class ShippingBox implements Boxable, Comparable & lt; ShippingBox & gt; { private String item; private double maxWeight; public ShippingBox(double maxWeight) { this.maxWeight = maxWeight; } public boolean add(String element) { if (item == null) { item = element; return true; } return false; } public String remove(String element) { String retVal = null; if (item != null & amp; & amp; item.equals(element)) { retVal = item; item = null; } return retVal; } public String peek() { if (item == null) { return "Shipping box is empty"; } return "Shipping: " + item; } @Override public int compareTo(ShippingBox other) { // close enough to equal if (Math.abs(this.maxWeight - other.maxWeight) & lt; 0.001) return 0; else if (this.maxWeight & gt; other.maxWeight) { return 100; // return something positive } else { return -100; // return something negative } } }
Abstract Classes
An abstract class is one that is not used to construct objects, but only as a basis for making subclasses. An abstract class exists only to express the common properties of all its subclasses. To be an abstract class, the class must contain one or more abstract methods. The abstract methods in the class are methods for which there is not a reasonable default implementation. Any class that extends an abstract class is required to implement the abstract method(s).
A class that is not abstract is said to be concrete. You can create objects belonging to a concrete class, but not to an abstract class. A variable whose type is given by an abstract class can only refer to objects that belong to concrete subclasses of the abstract class.
Example: Abstract Box
/** * Represents a box that holds one item */ public abstract class AbstractBox { private double width; private double length; private double height; /** * Initializes a box with dimensions length x width x height */ public AbstractBox(double length, double width, double height) { this.length = length; this.width = width; this.height = height; } /** * Adds element to the box, if it is empty * * @param element * @return true if element was added, false if the box was already full */ public abstract boolean add(String element); /** * Removes object from the box */ public abstract String remove(); @Override public String toString() { String bottom = "#".repeat(20) + "n"; String emptySide = "###" + " ".repeat(14) + "###" + "n"; return emptySide + emptySide + bottom; } }
Note you cannot create objects of type Box. You can, however, use Box as a reference type. The following class, MovingBox, extends Box. Note you can create objects of type MovingBox and use reference types of either Box or MovingBox to refer to a MovingBox object.
/** * Represents a box that holds one item */ public class MovingBox extends AbstractBox { private String contents; /** * Initializes a box with dimensions length x width x height */ public MovingBox(double length, double width, double height) { super(length, width, height); contents = null; } @Override public boolean add(String element) { if (contents != null) return false; contents = element; return true; } @Override public String remove() { return contents; } @Override public String toString() { String inside = String.format("### %10s ###n", contents); return inside + super.toString(); } }
Using the abstract Box class creates a more concrete definition of a box than the interface does. Every class that extends Box IS-A box. The abstract class provides specific instance variables and behaviors for most methods as everything (except the constructor) will be inherited by the subclass. Classes that have similar behavior but are not boxes (like a bowl) would not extend Box because a Bowl is not a Box.
Abstract Classes vs. Interfaces
Subclasses (child classes) have a different relationship between interfaces and abstract superclasses (parent classes). A subclass that implements an interface is saying simply that it “acts like” what specified by the interface. The class makes no statements however about fundamentally what it actually is. An actor implements a fearsome alien from a distant planet in one movie and a fickle feline in another. But an actor is actually neither. Just because the actor portrayed an interplanetary alien, doesn’t mean that the actor fundamentally possessed all the abilities of such an alien. All it says is that in so far the context in which the actor was utilized as the alien, the actor did implement all the necessary behaviors of the alien.
A subclass is fundamentally an example of its superclass. A subclass automatically contains all the behaviors of its superclass because it fundamentally is the superclass. The subclass doesn’t have to implement the behaviors of its superclass; it already has them. An actor is a human and by that right, automatically possesses all that which makes up a human: physical characteristics, emotions, ability to critically think, etc. Note that this is true even if the abstract class has 100% abstract methods – it still enforces a strict taxonomical hierarchy.
implements is about behaving, extends is about being.
Generics
One of the restrictions of our Box classes is that we are restricting what can be stored in the Box. However, nothing in either the Boxable Interface nor the abstract Box class actually depends on the type being stored in the Box. That data type can be abstracted away!
Generic programming is one way to allow arbitrary types to be used. Generics allow you to use the algorithm, which is not dependent on the data type, and let the compiler figure out/handle the actual data being used. We will look at how to implement classes using generic programming in the following sections to expand this definition. But first, let’s take a step back and consider how we can classify the Box class(es) – as an Abstract Data Type.
An instance of a class, that is, something that is created and takes up storage during the execution of a computer program. In the object-oriented programming paradigm, objects are the basic units of operation. Objects have state in the form of data members, and they know how to perform certain actions (methods).
In object-oriented programming, any class within a class hierarchy that inherits from some other class. Also known as a child class.
In object-oriented programming, a class from which another class inherits. Also called a base class or parent class.
Writing code that will work with various types of data, rather than with just a single type of data. The Java Collection Framework, and classes that use similar techniques, are examples of generic programming in Java.
Abbreviated ADT. The specification of a data type within some language, independent of an implementation. The interface for the ADT is defined in terms of a type and a set of operations on that type. The behavior of each operation is determined by its inputs and outputs. An ADT does not specify how the data type is implemented. These implementation details are hidden from the user of the ADT and protected from outside access, a concept referred to as encapsulation.