Java Interface Example
In this post, we feature a comprehensive Java Interface Example.
You can also check this tutorial in the following video:
1. Introduction
Java interface is an abstract type that defines methods that classes must implement. It can contain constants, abstract method signatures, default methods along with an implementation body, static methods along with an implementation body, and nested types. Java Interface
defines the “contracts” and represents the IS-A relationship. Interface design pattern is one of the best known patterns in software engineering.
Java defines lots of interfaces
. Interface
List is a well-known example. Java implements it with several implementation classes: AbstractList, AbstractSequentialList, ArrayList, AttributeList, CopyOnWriteArrayList, LinkedList, RoleList, RoleUnresolvedList, Stack, Vector.
In this example, I will create a maven project to demonstrate the following interface
features:
- Defining an
interface
which contains default, static, private, and abstract methods - Defining an
interface
which extends from one or moreinterfaces
- Creating an implementation class from a single
interface
- Creating an implementation class from multiple interfaces
- Utilizing the
interface
design pattern in an application
2. Technologies Used
The example code in this example was built and run with:
- Java 11
- Maven 3.3
- Eclipse
- Junit 4.12
- Logback
3. Maven Project
3.1 Dependencies
I will include both Junit
and Logback
in the pom.xml
.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>jcg-zheng-interface-demo</groupId> <artifactId>jcg-zheng-interface-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <build> <sourceDirectory>src</sourceDirectory> <plugins> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.8.0</version> <configuration> <release>11</release> </configuration> </plugin> </plugins> </build> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-access</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-classic</artifactId> <version>1.2.3</version> </dependency> <dependency> <groupId>ch.qos.logback</groupId> <artifactId>logback-core</artifactId> <version>1.2.3</version> </dependency> </dependencies> </project>
4. Interfaces
In this step, I will created five interfaces based on the following diagram.
4.1 MusicalInstrument
In this step, I will create a MusicalInstrument
interface
which contains:
static String PLAY_MSG
– a constant string value of ” plays “private static boolean isBroke()
– a private method which returns a true or false valuepublic static String repair()
– a public static method which repairs a musical instrument.default String demo()
– a default method which returns a messageString instrumentName()
– a public abstract method which every implementation class must implementvoid play(String song)
– a public abstract method which every implementation class must implementprivate String playMsg()
– a private method which returns aString
value
MusicalInstrument.java
package jcg.zheng.demo.api; import java.util.Random; /** * * Interface defines "contracts" * * JDK 8 * * 1) add Default interface method to preserve backward compatibility when * modifying an interface by adding new methods when the interface has more than * one implementations. Adding new methods will force all the implementation to * implement these new methods. With Default interface method solve this issue. * 2) add static method * * JDK 9 add private method * */ public interface MusicalInstrument { static String PLAY_MSG = " plays : "; private static boolean isBroke() { boolean isBroke = false; Random ran = new Random(); int nxt = ran.nextInt(); if (nxt % 8 == 0) { isBroke = true; } return isBroke; } /** * repair the instrument * * @param isBroke * @return a message show the repair result; */ public static String repair() { if (isBroke()) { return "Fixed"; } return "In good condition"; } /** * Play demo song * * @return */ default String demo() { return instrumentName() + playMsg(); } /** * Return the musical instrument name * * @return the musical instrument name */ String instrumentName(); /** * Play the given song * * @param song the song to play */ void play(String song); private String playMsg() { return " plays a demo song"; } }
4.2 BrassInstrument
In this step, I will create a BrassInstrument
interface
which extends from MusicalInstrument
with an additional buzz(String song)
method.
BrassInstrument.java
package jcg.zheng.demo.api; public interface BrassInstrument extends MusicalInstrument { /** * Make a buzz sound for the given song * * @param song the given song */ void buzz(String song); }
4.3 StringInstrument
In this step, I will create a StringInstrument
interface
which extends from MusicalInstrument
along with two additional methods: bow(String song)
and pluck(String song)
.
StringInstrument.java
package jcg.zheng.demo.api; public interface StringInstrument extends MusicalInstrument { /** * Play the song with a bow * * @param song */ void bow(String song); /** * Play the song by plucking * * @param song */ void pluck(String song); }
4.4 ChildrenToy
In this step, I will create a ChildrenToy
interface
which has one default method – demo
.
ChildrenToy.java
package jcg.zheng.demo.api; public interface ChildrenToy { /** * Play the demo song * * @return the demo song */ default String demo() { return "Play the demo song"; } }
4.5 PercussionInstrument
In this step, I will create a PercussionInstrument
interface
which extends from both MusicalInstrument
and ChildrenToy
interfaces. I will override the demo
method because both parent interfaces have the demo
method.
PercussionInstrument.java
package jcg.zheng.demo.api; public interface PercussionInstrument extends MusicalInstrument, ChildrenToy { /** * use the MusicalInstrument demo sine both interface has demo */ @Override default String demo() { return MusicalInstrument.super.demo(); } /** * Play the song by hitting * * @param song */ void hit(String song); /** * Play the song by shaking * * @param song */ void shake(String song); }
5. Implementation Classes
In this step, I will create nine implementation classes base on the following diagram.
5.1 Bell
In this step, I will create a Bell
class which implements the PercussionInstrument
interface.
Bell.java
package jcg.zheng.demo.api.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jcg.zheng.demo.api.PercussionInstrument; public class Bell implements PercussionInstrument { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public void hit(String song) { logger.info(instrumentName() + PLAY_MSG + song); } @Override public String instrumentName() { return "Bell"; } @Override public void play(String song) { shake(song); } @Override public void shake(String song) { logger.info(instrumentName() + PLAY_MSG + song); } }
5.2 Drum
In this step, I will create a Drum
class which implements the PercussionInstrument
interface.
Drum.java
package jcg.zheng.demo.api.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jcg.zheng.demo.api.PercussionInstrument; public class Drum implements PercussionInstrument { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public void hit(String song) { logger.info(instrumentName() + PLAY_MSG + song); } @Override public String instrumentName() { return "Drum"; } @Override public void play(String song) { hit(song); } @Override public void shake(String song) { throw new UnsupportedOperationException(); } }
5.3 KidDrumToy
In this step, I will create a KidDrumToy
class which implements the PercussionInstrument
interface.
KidDrumToy.java
package jcg.zheng.demo.api.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jcg.zheng.demo.api.PercussionInstrument; public class KidDrumToy implements PercussionInstrument { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public String demo() { return "KidDrumToy play the demo song by hitting and shaking"; } @Override public void hit(String song) { logger.info(instrumentName() + PLAY_MSG + song + " when is hit."); } @Override public String instrumentName() { return "KidMusicToy"; } @Override public void play(String song) { hit(song); shake(song); } @Override public void shake(String song) { logger.info(instrumentName() + PLAY_MSG + song + " when is shaked."); } }
5.4 KidMusicalToy
In this step, I will create a KidMusicalToy
class which implements both PercussionInstrument
and StringInstrument
interfaces.
KidMusicalToy.java
package jcg.zheng.demo.api.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jcg.zheng.demo.api.BrassInstrument; import jcg.zheng.demo.api.StringInstrument; public class KidMusicalToy implements BrassInstrument, StringInstrument { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public void bow(String song) { logger.info("play a string with a bow " + song); } @Override public void buzz(String song) { logger.info("make a buzz sound " + song); } @Override public String demo() { return "KidMusicToy play the demo song with String and buzz"; } @Override public String instrumentName() { return "KidMusicToy"; } @Override public void play(String song) { bow(song); pluck(song); buzz(song); } @Override public void pluck(String song) { logger.info("play a string with a pluck sound " + song); } }
5.5 Trumpet
In this step, I will create a Trumpet
class which implements the BrassInstrument
interface.
Trumpet.java
package jcg.zheng.demo.api.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jcg.zheng.demo.api.BrassInstrument; public class Trumpet implements BrassInstrument { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public void buzz(String song) { logger.info(instrumentName() + PLAY_MSG + song); } @Override public String instrumentName() { return "Trumpet"; } @Override public void play(String song) { buzz(song); } }
5.6 Tuba
In this step, I will create a Tuba
class which implements the BrassInstrument
interface.
Tuba.java
package jcg.zheng.demo.api.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jcg.zheng.demo.api.BrassInstrument; public class Tuba implements BrassInstrument { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public void buzz(String song) { logger.info(instrumentName() + PLAY_MSG + song); } @Override public String demo() { return "Tuba overrides the default demo method"; } @Override public String instrumentName() { return "Tuba"; } @Override public void play(String song) { buzz(song); } }
5.7 Viola
In this step, I will create a Viola
class which implements the StringInstrument
interface.
Viola.java
package jcg.zheng.demo.api.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jcg.zheng.demo.api.StringInstrument; public class Viola implements StringInstrument { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public void bow(String song) { logger.info(instrumentName() + PLAY_MSG + song); } @Override public String instrumentName() { return "Viola"; } @Override public void play(String song) { bow(song); } @Override public void pluck(String song) { throw new UnsupportedOperationException(); } }
5.8 Violin
In this step, I will create a Violin
class which implements the StringInstrument
interface.
Violin.java
package jcg.zheng.demo.api.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jcg.zheng.demo.api.StringInstrument; public class Violin implements StringInstrument { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public void bow(String song) { logger.info(instrumentName() + PLAY_MSG + song); } @Override public String instrumentName() { return "Violin"; } @Override public void play(String song) { bow(song); } @Override public void pluck(String song) { throw new UnsupportedOperationException(); } }
5.9 Violin2
In this step, I will create a Violin2
class which implements the StringInstrument
interface
.
Violin2.java
package jcg.zheng.demo.api.impl; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import jcg.zheng.demo.api.StringInstrument; public class Violin2 implements StringInstrument { private final Logger logger = LoggerFactory.getLogger(this.getClass()); @Override public void bow(String song) { logger.info(instrumentName() + PLAY_MSG + song); } @Override public String instrumentName() { return "Violin2"; } @Override public void play(String song) { bow(song); } @Override public void pluck(String song) { throw new UnsupportedOperationException(); } }
6. Junit Tests
6.1 AbstractionTest
In this step, I will create an AbstractionTest
class which has a list of MusicalInstrument
s and invokes its default demo
method and abstract play
method. I will use the logger
to print out the messages.
AbstractionTest.java
package jcg.zheng.demo.api.impl; import java.util.ArrayList; import java.util.List; import org.junit.Before; import org.junit.Test; import jcg.zheng.demo.api.MusicalInstrument; public class AbstractionTest { List<MusicalInstrument> musicInstruments = new ArrayList<>(); @Test public void polymorphism_with_abstract_method() { musicInstruments.forEach(mi -> mi.play("Hello")); } @Test public void polymorphism_with_default_method() { musicInstruments.forEach(mi -> System.out.println(mi.demo())); } @Before public void setupBand() { musicInstruments.add(new Violin()); musicInstruments.add(new Viola()); musicInstruments.add(new Bell()); musicInstruments.add(new Drum()); musicInstruments.add(new Trumpet()); musicInstruments.add(new Tuba()); musicInstruments.add(new KidMusicalToy()); musicInstruments.add(new KidDrumToy()); musicInstruments.add(new Violin2()); } }
Execute mvn test -Dtest=AbstractionTest
command and capture the output here.
Output
C:\MaryZheng\Workspaces\jdk12\jcg-zheng-interface-demo>mvn test -Dtest=AbstractionTest [INFO] Scanning for projects... [INFO] [INFO] ---------< jcg-zheng-interface-demo:jcg-zheng-interface-demo >---------- [INFO] Building jcg-zheng-interface-demo 0.0.1-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ jcg-zheng-interface-demo --- [WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory C:\MaryZheng\Workspaces\jdk12\jcg-zheng-interface-demo\src\main\resources [INFO] [INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ jcg-zheng-interface-demo --- [INFO] Changes detected - recompiling the module! [WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent! [INFO] Compiling 17 source files to C:\MaryZheng\Workspaces\jdk12\jcg-zheng-interface-demo\target\classes [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ jcg-zheng-interface-demo --- [WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory C:\MaryZheng\Workspaces\jdk12\jcg-zheng-interface-demo\src\test\resources [INFO] [INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ jcg-zheng-interface-demo --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ jcg-zheng-interface-demo --- [INFO] Surefire report directory: C:\MaryZheng\Workspaces\jdk12\jcg-zheng-interface-demo\target\surefire-reports ------------------------------------------------------- T E S T S ------------------------------------------------------- Running jcg.zheng.demo.api.impl.AbstractionTest Violin plays a demo song Viola plays a demo song Bell plays a demo song Drum plays a demo song Trumpet plays a demo song Tuba overrides the default demo method KidMusicToy play the demo song with String and buzz KidDrumToy play the demo song by hitting and shaking Violin2 plays a demo song 21:34:55.464 [main] INFO jcg.zheng.demo.api.impl.Violin - Violin plays : Hello 21:34:55.472 [main] INFO jcg.zheng.demo.api.impl.Viola - Viola plays : Hello 21:34:55.473 [main] INFO jcg.zheng.demo.api.impl.Bell - Bell plays : Hello 21:34:55.476 [main] INFO jcg.zheng.demo.api.impl.Drum - Drum plays : Hello 21:34:55.478 [main] INFO jcg.zheng.demo.api.impl.Trumpet - Trumpet plays : Hello 21:34:55.482 [main] INFO jcg.zheng.demo.api.impl.Tuba - Tuba plays : Hello 21:34:55.497 [main] INFO jcg.zheng.demo.api.impl.KidMusicalToy - play a string with a bow Hello 21:34:55.498 [main] INFO jcg.zheng.demo.api.impl.KidMusicalToy - play a string with a pluck sound Hello 21:34:55.499 [main] INFO jcg.zheng.demo.api.impl.KidMusicalToy - make a buzz sound Hello 21:34:55.516 [main] INFO jcg.zheng.demo.api.impl.KidDrumToy - KidMusicToy plays : Hello when is hit. 21:34:55.517 [main] INFO jcg.zheng.demo.api.impl.KidDrumToy - KidMusicToy plays : Hello when is shaked. 21:34:55.519 [main] INFO jcg.zheng.demo.api.impl.Violin2 - Violin2 plays : Hello Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.577 sec Results : Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 9.610 s [INFO] Finished at: 2019-06-25T21:34:55-05:00 [INFO] ------------------------------------------------------------------------
6.2 RuntimeTest
In this step, I will create a RuntimeTest
class which will create a different Violin
class based on the condition.
RuntimeTest.java
package jcg.zheng.demo.api.impl; import static org.junit.Assert.assertEquals; import org.junit.Test; import jcg.zheng.demo.api.MusicalInstrument; public class RuntimeTest { @Test public void runtime_injection() { MusicalInstrument violin = violinBuilder(false); assertEquals(Violin.class, violin.getClass()); } @Test public void runtime_injection_2() { MusicalInstrument violin = violinBuilder(true); assertEquals(Violin2.class, violin.getClass()); } private MusicalInstrument violinBuilder(boolean isVersion2) { MusicalInstrument violin; if (isVersion2) { violin = new Violin2(); } else { violin = new Violin(); } return violin; } }
Execute mvn test -Dtest=RuntimeTest
command and capture the output here.
Output
C:\MaryZheng\Workspaces\jdk12\jcg-zheng-interface-demo>mvn test -Dtest=RuntimeTest [INFO] Scanning for projects... [INFO] [INFO] ---------< jcg-zheng-interface-demo:jcg-zheng-interface-demo >---------- [INFO] Building jcg-zheng-interface-demo 0.0.1-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ jcg-zheng-interface-demo --- [WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory C:\MaryZheng\Workspaces\jdk12\jcg-zheng-interface-demo\src\main\resources [INFO] [INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ jcg-zheng-interface-demo --- [INFO] Changes detected - recompiling the module! [WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent! [INFO] Compiling 17 source files to C:\MaryZheng\Workspaces\jdk12\jcg-zheng-interface-demo\target\classes [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ jcg-zheng-interface-demo --- [WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory C:\MaryZheng\Workspaces\jdk12\jcg-zheng-interface-demo\src\test\resources [INFO] [INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ jcg-zheng-interface-demo --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ jcg-zheng-interface-demo --- [INFO] Surefire report directory: C:\MaryZheng\Workspaces\jdk12\jcg-zheng-interface-demo\target\surefire-reports ------------------------------------------------------- T E S T S ------------------------------------------------------- Running jcg.zheng.demo.api.impl.RuntimeTest Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.338 sec Results : Tests run: 2, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 9.608 s [INFO] Finished at: 2019-06-25T21:42:03-05:00 [INFO] ------------------------------------------------------------------------
6.3 StaticMethodTest
In this step, I will create a StaticMethodTest
class to test the interface’s static method.
StaticMethodTest.java
package jcg.zheng.demo.api.impl; import org.junit.Test; import jcg.zheng.demo.api.MusicalInstrument; public class StaticMethodTest { @Test public void test_static_method() { System.out.println(MusicalInstrument.repair()); } }
Execute mvn test -Dtest=StaticMethodTest
command and capture the output here.
Output
C:\MaryZheng\Workspaces\jdk12\jcg-zheng-interface-demo>mvn test -Dtest=StaticMethodTest [INFO] Scanning for projects... [INFO] [INFO] ---------< jcg-zheng-interface-demo:jcg-zheng-interface-demo >---------- [INFO] Building jcg-zheng-interface-demo 0.0.1-SNAPSHOT [INFO] --------------------------------[ jar ]--------------------------------- [INFO] [INFO] --- maven-resources-plugin:2.6:resources (default-resources) @ jcg-zheng-interface-demo --- [WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory C:\MaryZheng\Workspaces\jdk12\jcg-zheng-interface-demo\src\main\resources [INFO] [INFO] --- maven-compiler-plugin:3.8.0:compile (default-compile) @ jcg-zheng-interface-demo --- [INFO] Changes detected - recompiling the module! [WARNING] File encoding has not been set, using platform encoding Cp1252, i.e. build is platform dependent! [INFO] Compiling 17 source files to C:\MaryZheng\Workspaces\jdk12\jcg-zheng-interface-demo\target\classes [INFO] [INFO] --- maven-resources-plugin:2.6:testResources (default-testResources) @ jcg-zheng-interface-demo --- [WARNING] Using platform encoding (Cp1252 actually) to copy filtered resources, i.e. build is platform dependent! [INFO] skip non existing resourceDirectory C:\MaryZheng\Workspaces\jdk12\jcg-zheng-interface-demo\src\test\resources [INFO] [INFO] --- maven-compiler-plugin:3.8.0:testCompile (default-testCompile) @ jcg-zheng-interface-demo --- [INFO] Nothing to compile - all classes are up to date [INFO] [INFO] --- maven-surefire-plugin:2.12.4:test (default-test) @ jcg-zheng-interface-demo --- [INFO] Surefire report directory: C:\MaryZheng\Workspaces\jdk12\jcg-zheng-interface-demo\target\surefire-reports ------------------------------------------------------- T E S T S ------------------------------------------------------- Running jcg.zheng.demo.api.impl.StaticMethodTest In good condition Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.123 sec Results : Tests run: 1, Failures: 0, Errors: 0, Skipped: 0 [INFO] ------------------------------------------------------------------------ [INFO] BUILD SUCCESS [INFO] ------------------------------------------------------------------------ [INFO] Total time: 9.069 s [INFO] Finished at: 2019-06-25T21:43:10-05:00 [INFO] ------------------------------------------------------------------------
7. Java Interface Example – Summary
In this example, I demonstrated how to define interfaces and create its implementation classes. Java interface provides the following benefits:
- Support polymorphism by sharing a standard set of methods in different ways.
- Support encapsulation by separating the method definition from the implementation.
- Supports multiple inheritances.
- Allow dependency injections during runtime
8. Download the Source Code
This example consists of a Maven project which defines five interfaces and nine implementation classes and three junit test classes to show the interface abstraction and runtime object injection.
You can download the full source code of this example here: Java Interface Example
Last updated on Feb. 12th, 2020