Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Private interfaces inside a class

Here I came across this phrase:

Implementing a private interface is a way to force the definition of the methods in that interface without adding any type information (that is, without allowing any upcasting).

I'm finding it difficult to understand this. Can some one explain this for me?

like image 744
tharindu_DG Avatar asked Dec 20 '14 11:12

tharindu_DG


2 Answers

Here is an example of private interfaces.

public class Main  {

    private interface Animal {
        void makeNoise();
    }

    public static final class Cow implements Animal {
        @Override
        public void makeNoise() {
            System.out.println("Moo!");
        }
    }

    public static final class Sheep implements Animal {
        @Override
        public void makeNoise() {
            System.out.println("Bah!");
        }
    }

    public static void main(String[] args) {
        List<Animal> animals = Arrays.asList(new Cow(), new Sheep());
        for (Animal animal : animals)
            animal.makeNoise();
    }
}    

From within the class Main you can refer to an Animal and call makeNoise() on it. Therefore you can have a List of Animals of different types and use a for each loop to call makeNoise() on them all.

However, outside the class Main this is not possible. You can have a Cow or a Sheep and call makeNoise() on either, but the interface Animal and the interface method makeNoise() are invisible.

like image 131
Paul Boddington Avatar answered Oct 25 '22 19:10

Paul Boddington


The examples in the linked article are a bit contrived and artificial (as already indicated by the odd names, A, B etc...). However, let's focus on the part of the quote that your question refers to:

"...without adding any type information (that is, without allowing any upcasting)."

The class may offer multiple (public or private) implementations of this interface. But the key point is:

Nobody will ever be able to figure out that they implement this interface.

Simply, because the interface is not public.

I tried to create an example showing a possible application case. Of course, it is still contrived, but may make the point more obvious. Consider you want to model a Tree data structure, which consists of Node objects. These can be InnerNode objects (which have child nodes) or LeafNode objects (which have no children).

Such a class could be implemented like this:

class Tree {

    // The private interface
    private interface Node {
        List<Node> getChildren();
    }      

    // Both are public implementations
    public class InnerNode implements Node {
        @Override 
        public List<Node> getChildren() {  
            return Arrays.<Node>asList(getLeafNode(), getLeafNode());
        }
    }
    public class LeafNode implements Node {
        @Override 
        public List<Node> getChildren() {  
            return Collections.emptyList();
        }
    }

    // These return the concrete, public types
    public InnerNode getInnerNode() { return new InnerNode(); }
    public LeafNode  getLeafNode()  { return new LeafNode();  }

    // This returns the private interface type
    public Node getRootNode() { 

        // Both concrete types can be returned here,
        // because they both implement the interface
        return getInnerNode(); // Works 
        //return getLeafNode(); // Works
    }

    // This uses only the interface type
    public void traverseNode(Node node) {
        System.out.println("Traversing "+node);
        for (Node child : node.getChildren()) {
            traverseNode(child);
        }
    }
}

In an external main method, you can observe the limitations imposed by the private interface:

public static void main(String[] args) {
    Tree tree = new Tree();

    // The public concrete types can be used
    Tree.LeafNode  leafNode  = tree.getLeafNode();
    Tree.InnerNode innerNode = tree.getInnerNode();

    // The private interface can not be used from outside:
    //Tree.Node node = tree.getRootNode();

    // This is possible: The class only uses its 
    // own private interface here
    tree.traverseNode(tree.getRootNode());
}

In this example, you can call traverseNode, passing in the Node that is returned by getRootNode, regardless of whether this node is a InnerNode or a LeafNode. In the current version, this will print something like

Traversing Tree$InnerNode
Traversing Tree$LeafNode
Traversing Tree$LeafNode

If you changed getRootNode to return a LeafNode, then it would only print

Traversing Tree$LeafNode

To put it simply, and as the name "private interface" already suggests: You can use this in order to hide the fact that two classes share a common ancestor.

like image 20
Marco13 Avatar answered Oct 25 '22 19:10

Marco13