From charlesreid1

Interfaces: https://docs.oracle.com/javase/tutorial/java/concepts/interface.html

Also of interest in Java 8: the line between abstract classes and interfaces just got a little thinner: https://stackoverflow.com/questions/31578427/what-is-the-purpose-of-the-default-keyword-in-java#31578471

See Java/Default keyword

What are interfaces

I like to describe interfaces as inheritance lite - or super polymorphism. They provide some useful guarantees about the way an interface to a class will work, without the restrictive requirements that come with inheritance. A classic example is an interface indicating something can make noise - babies and monster trucks both make noise, but there is not much more they have in common, ontologically.

An interface requires a class to provide certain methods. A good example of this is for Trees data structures (see also Data Structures and Abstract Data Types). The abstract implementation of methods that any tree should implement, but that will depend on their concrete implementation, can be defined by an interface, and actually implemented in the class that implements the interface.

The "super polymorphism" part comes from the fact that we can now define a method that takes a parameter that is of the Interface type, if all it needs to do is call the method defined by the interface. Now, we can use one method to implement the one behavior in a whole range of different, otherwise-unrelated objects.

Applications

Interfaces are useful for data containers - that's a situation where you often want a consistent interfaces to different types of objects that may have very different underlying functionality.

Another example would be weight. Imagine we ask for the weight of an object. This might be a sack of dried peas or an elephant, a pencil sharpener or a black hole, all we really require is that it is a noun. (We cannot measure the weight of the color blue.) We can define a Noun interface that requires all Nouns to implement a weight() function. How they do it is up to them, and ontologically there's nothing else that links a black hole and a pencil sharpener, but for our purposes, that's perfectly fine.

If we are defining a function in another class that needs the weight of an object, now we don't need to define a wrapper function for each different type (or parent type) of object we might see - PencilMiscellany parent types and AstronomicalObject parent types and etc. - we just need to write an interface that accepts one Noun (interface) object.

Toy Examples

Two toy examples that explain well the kind of relationships that interfaces describe, and why they make it easy to use collections of specialized pointers:

Babies and Monster Trucks

Suppose we have two classes, the Baby class and the MonsterTruck class. Both classes implement things that make noise, and if our goal is to disturb someone who is trying to sleep, and we have a container full of things that make noise, all we care about is that we are able to use them to make noise. It does not matter how. Thus, we can define a MakesNoise interface that requires our object implements a makeNoise() method. Then we can handle a container full of MakesNoise pointers and use them to bother someone who is trying to sleep using every means at our disposal, be it baby trucks or monsters. Er...

Flies and Telemarketers

Here is another example: https://stackoverflow.com/a/384067

This implements a pest interface that requires an object implement a method to be annoying. It then implements a house fly and telemarketer implementation of the Pest interface:

 interface Pest {
    void BeAnnoying();
 }

 class HouseFly inherits Insect implements Pest {
   void FlyAroundYourHead(){}
   void LandOnThings(){}

   void BeAnnoying() {
     FlyAroundYourHead();
     LandOnThings();
   }
 }

 class Telemarketer inherits Person implements Pest {
   void CallDuringDinner(){}
   void ContinueTalkingWhenYouSayNo(){}

   void BeAnnoying() {
      CallDuringDinner();
      ContinueTalkingWhenYouSayNo();
   }
 }

This finally allows the implementation of a container full of Pests, which can be iterated over so that their annoying behavior is implemented.

class DiningRoom {

   DiningRoom(Person[] diningPeople, Pest[] pests) { ... }

   void ServeDinner() {
     when diningPeople are eating,

       foreach pest in pests
         pest.BeAnnoying();
   }
 }

Data Structures Example - Tree

Let's look at a simple example of an interface for a tree and tree abstract data type. The interface does not do anything terribly fancy, just describes a size and an isEmpty method, but this enables us to use Tree pointers when we just need to ensure that an object has a size() method.

The interface:

public interface Tree {
	public int size();
	public boolean isEmpty();
}

The abstract class that implements this interface:

public abstract class AbstractTree<E> implements Tree {
	int size;
	char parity;

	/** Boolean method definition, required by Tree interface. */
	public boolean isEmpty() {
		return size()==0;
	}

	/** Int method definition, required by Tree interface. */
	public int size() {
		return this.size;
	}

	/** Access method not defined in Tree interface. */
	public void setParity(char c){
		this.parity = c;
	}
}

The concrete class that implements the abstract interface:

class Empty extends IllegalStateException {}

public class ConcreteTree<E> extends AbstractTree<E> {

	public ConcreteTree(int n) {
		this.size = n;
		this.setParity('x');
	}

	/** Print "parity" of this thing (whatever that is). */
	public void printParity() {
		if(isEmpty()) {
			throw new Empty();
		}
		System.out.println("Parity: "+this.parity);
	}

	/** Print size of this thing. */
	public void printSize() {
		if(isEmpty()) {
			throw new Empty();
		} else {
			System.out.println("Size: "+this.size);
		}
	}

	public static void main(String[] args) {
		ConcreteTree<String> c = new ConcreteTree<String>(5);
		c.printSize();
		c.printParity();
	}
}

Flags





See also: