Saturday, January 12, 2008

Java Multiple Interface Basics

Everyone once in a while, I like to write up a quick blog entry on a basic topic that I get fairly frequently asked about or otherwise talk to people about. One significant and basic Java concept that I find is often not well understood, especially by people relatively new to Java, is that of the Java interface. In this blog entry, I am going to show a class that implements two Java interfaces. Both interfaces are very simple and actually define only a single method each. What makes the example even more interesting is that the method is defined exactly the same in each interface with an identical method signature. The implementation class's method with the matching name implements the same-named method in both implemented interfaces.

The first code sample is for the Java interface InterfaceOne.java:


package interfaces;

public interface InterfaceOne
{
void doSomethingSwell();
}


While it doesn't get much easier than the code example shown above, InterfaceTwo.java is just as easy and is shown next:


package interfaces;

public interface InterfaceTwo
{
void doSomethingSwell();
}


Both InterfaceOne and InterfaceTwo define a method with the same signature. The next code sample shows Java class Implementation.java that implements both interfaces.


package interfaces;

/**
* This class demonstrates how multiple Java interfaces specifying the same
* method (same name and other signature characteristics) can be implemented by
* a single class.
*/
public class Implementation implements InterfaceOne, InterfaceTwo
{
public Implementation()
{
System.out.println("Implementation Constructor");
}

@Override
public void doSomethingSwell()
{
System.out.println("Implementation.doSomethingSwell");
}

public static void doOnlyForOne( InterfaceOne aOne )
{
System.out.println("doOnlyForOne(InterfaceOne)");
}
/*
// This must be commented out so as not to be ambiguous because this
// Implementation class extends both interfaces and so trying to pass an
// instance of this class to this method with both versions uncommented
// results in an ambiguity error.
public static void doOnlyForOne( InterfaceTwo aTwo )
{
System.out.println("doOnlyForOne(InterfaceTwo)");
}
*/

public static void main(String aArgs[])
{
final String lineBreak = System.getProperty("line.separator");
System.out.println(lineBreak);

System.out.print("Direct Implementation: " );
Implementation me = new Implementation();
me.doSomethingSwell();
doOnlyForOne(me);
System.out.print("Direct Implementation is instance of InterfaceOne? ");
System.out.println( me instanceof InterfaceOne );
System.out.print("Direct Implementation is instance of InterfaceTwo? ");
System.out.println( me instanceof InterfaceTwo );
System.out.println( lineBreak );

System.out.print("InterfaceOne: ");
InterfaceOne one = new Implementation();
one.doSomethingSwell();
doOnlyForOne(one);
System.out.print("Implementation 'one' is instance of InterfaceOne? ");
System.out.println( one instanceof InterfaceOne );
System.out.print("Implementation 'one' is instance of InterfaceTwo? ");
System.out.println( one instanceof InterfaceTwo );
System.out.println( lineBreak );

System.out.print("InterfaceTwo: ");
InterfaceTwo two = new Implementation();
two.doSomethingSwell();
// Must comment out line below once the doOnlyForOne expecting an
// InterfaceTwo was commented out.
//doOnlyForOne(two);
System.out.print("Implementation 'two' is instance of InterfaceOne? ");
System.out.println( two instanceof InterfaceOne );
System.out.print("Implementation 'two' is instance of InterfaceTwo? ");
System.out.println( two instanceof InterfaceTwo );
}
}


To make the Java class and the two interfaces it implements compile, I needed to comment out the portion of the code that describes a second doOnlyForOne method. Although this commented-out method had a different argument type (InterfaceTwo rather than InterfaceOne), the Implementation class implements both and so the compiler has no way of knowing which one is appropriate. The output when trying to compile the code without that second doOnlyForOne method commented out is shown next (click on this image to see larger version):



Commenting out the ambiguous method removes the error shown above, but now there is an error because the call to doOnlyForOne(InterfaceOne) no longer applies to the Implementation instance that is assigned as InterfaceTwo on instantiation. When this code is not commented out, the following error is seen:



With the call to doOnlyForOne(InterfaceTwo) commented out, the code compiles and the executable can be run. The next image shows the execution of this simple code and the output.



When the output is compared to the code, we see evidence of many of the basic principles of Java interfaces. Among other things, we can see that an Implementation class is known to be an implementation of both interfaces (using instanceof) even when it is only treated as a single interface. We also see in this example that a single implementation method can satisfy multiple interfaces' declaration of that method if the signatures all match. Finally, the example demonstrated the nuance of multiple implemented interfaces that can lead to ambiguous methods calls for seemingly unrelated interfaces if appropriate naming is not used.

When I am explaining Java interfaces to someone new to Java, I usually like to show that person use of a Java collection directly versus using its interface and then demonstrate how much easier it is to swap out collection implementations if interfaces are what are passed around and returned from methods. However, simple examples like those shown above also seem to go a long way toward explaining how these nifty Java interfaces really work.

No comments: