1. Interfaces in Java
An interface in Java is a blueprint used for standardization and abstraction. It contains a contract that implementing classes must follow.
- An interface is used for standardization.
- It contains a skeleton that is implemented by a class.
- An interface is a blueprint of a class.
- It is used to achieve abstraction and support multiple inheritance through interfaces.
- One class can implement multiple interfaces.
- One interface can extend multiple interfaces.
- Interface fields are public, static and final by default.
- Traditional interface methods are public and abstract by default.
- An interface is used by the
implementskeyword. - An interface cannot be instantiated directly and does not have constructors.
Why Use Java Interfaces?
- To achieve abstraction.
- To support multiple inheritance of type.
- To achieve loose coupling.
- To improve consistency and object-oriented design.
2. Understanding the Relationship Between Classes and Interfaces
Java uses different keywords for relationships between classes and interfaces:
| Relationship | Keyword |
|---|---|
| Class extends another class | extends |
| Class implements an interface | implements |
| Interface extends another interface | extends |
A class can implement multiple interfaces, while an interface can extend multiple interfaces. This provides a way to model multiple inheritance of type in Java.
3. Types of Interfaces in Java
The PDF identifies three main categories:
1. Normal Interface
The regular form of interface used to define method contracts.
2. Functional Interface
An interface with exactly one abstract method, suitable for lambda expressions and method references.
3. Marker Interface
An empty interface used to mark a class for special treatment by the runtime or frameworks.
Marker Interface
A marker or tagging interface contains no methods or fields. Its purpose is to associate metadata with a class and indicate that it has a particular property.
Examples: Serializable, Cloneable, Remote.
4. Normal Interface
A normal or regular interface is the standard form used to define method signatures and establish a contract for implementing classes.
Example: Printable
interface Printable {
void print();
}
class A6 implements Printable {
public void print() {
System.out.println("Hello");
}
public static void main(String args[]) {
A6 obj = new A6();
obj.print();
}
}
Example: Drawable
interface Drawable {
void draw();
}
class Rectangle implements Drawable {
public void draw() {
System.out.println("drawing rectangle");
}
}
class Circle implements Drawable {
public void draw() {
System.out.println("drawing circle");
}
}
class TestInterface1 {
public static void main(String args[]) {
Drawable d = new Circle();
d.draw();
}
}
5. Multiple Inheritance in Java by Interface
If a class implements multiple interfaces, or an interface extends multiple interfaces, Java can model multiple inheritance of type.
Example: Bank Interface
interface Bank {
float rateOfInterest();
}
class SBI implements Bank {
public float rateOfInterest() {
return 9.15f;
}
}
class PNB implements Bank {
public float rateOfInterest() {
return 9.7f;
}
}
class TestInterface2 {
public static void main(String[] args) {
Bank b = new SBI();
System.out.println("ROI: " + b.rateOfInterest());
}
}
Example: Implementing Multiple Interfaces
interface Printable {
void print();
}
interface Showable {
void show();
}
class A7 implements Printable, Showable {
public void print() {
System.out.println("Hello");
}
public void show() {
System.out.println("Welcome");
}
public static void main(String args[]) {
A7 obj = new A7();
obj.print();
obj.show();
}
}
6. Interface Inheritance
An interface can extend another interface. A class implementing the child interface must provide implementations for the abstract methods inherited from the parent interface.
interface Printable {
void print();
}
interface Showable extends Printable {
void show();
}
class TestInterface4 implements Showable {
public void print() {
System.out.println("Hello");
}
public void show() {
System.out.println("Welcome");
}
public static void main(String args[]) {
TestInterface4 obj = new TestInterface4();
obj.print();
obj.show();
}
}
Default Methods
Modern Java interfaces can contain default methods with an implementation.
interface Drawable {
void draw();
default void msg() {
System.out.println("default method");
}
}
class Rectangle implements Drawable {
public void draw() {
System.out.println("drawing rectangle");
}
}
class TestInterfaceDefault {
public static void main(String args[]) {
Drawable d = new Rectangle();
d.draw();
d.msg();
}
}
7. Default Methods in Interfaces
A default method has a method body directly inside the interface. Implementing classes inherit the default implementation unless they override it.
8. Static Methods in Interfaces
Interfaces can also contain static methods. Static interface methods belong to the interface itself and are called using the interface name.
Example
interface Drawable {
void draw();
static int cube(int x) {
return x * x * x;
}
}
class Rectangle implements Drawable {
public void draw() {
System.out.println("drawing rectangle");
}
}
class TestInterfaceStatic {
public static void main(String args[]) {
Drawable d = new Rectangle();
d.draw();
System.out.println(Drawable.cube(3));
}
}
Output
drawing rectangle 27
Abstract Class vs Interface
| Abstract Class | Interface |
|---|---|
| Can contain abstract and non-abstract methods. | Can contain abstract methods plus default and static methods. |
| Does not support multiple inheritance of classes. | Supports multiple inheritance of type through interfaces. |
| Can have final, non-final, static and non-static variables. | Fields are public, static and final by default. |
| Can provide implementations and can implement interfaces. | Defines a contract and can extend other interfaces. |
Declared using the abstract keyword. | Declared using the interface keyword. |
| Can extend a Java class and implement interfaces. | Can extend other Java interfaces. |
Subclass uses extends. | Class uses implements. |
| Can have members with different access modifiers. | Interface members are public by default where applicable. |
9. Functional Interface / SAM Interface
A functional interface has exactly one abstract method. It is also called a SAM (Single Abstract Method) interface.
- It has exactly one abstract method.
- It can have multiple default or static methods.
- It can be implemented using lambda expressions and method references.
- The
@FunctionalInterfaceannotation is optional but recommended. - The annotation causes a compiler error if the interface contains more than one abstract method.
Example
@FunctionalInterface
public interface Predicate<T> {
boolean test(T t);
}
Examples of Functional Interfaces
- Runnable — contains
run() - Comparable — contains
compareTo() - ActionListener — contains
actionPerformed() - Callable — contains
call()
10. Functional Interface Lambda Examples
Predicate
public class PredicateExample {
public static void main(String[] args) {
Predicate<String> isLongerThan5 = s -> s.length() > 5;
System.out.println(isLongerThan5.test("Hello")); // false
System.out.println(isLongerThan5.test("Functional")); // true
}
}
BiPredicate
public class BiPredicateExample {
public static void main(String[] args) {
BiPredicate<String, Integer> isLengthEqual =
(str, len) -> str.length() == len;
System.out.println(isLengthEqual.test("Java", 4)); // true
System.out.println(isLengthEqual.test("Spring", 3)); // false
}
}
Function
public class FunctionExample {
public static void main(String[] args) {
Function<String, Integer> stringLength = s -> s.length();
System.out.println(stringLength.apply("Java")); // 4
System.out.println(stringLength.apply("Function")); // 8
}
}
BiFunction
public class BiFunctionExample {
public static void main(String[] args) {
BiFunction<Integer, Integer, Integer> add =
(a, b) -> a + b;
System.out.println(add.apply(10, 20)); // Output: 30
System.out.println(add.apply(5, 3)); // Output: 8
}
}
Consumer
public class ConsumerExample {
public static void main(String[] args) {
Consumer<String> greeter =
name -> System.out.println("Hello, " + name);
greeter.accept("Alice"); // Hello, Alice
greeter.accept("Bob"); // Hello, Bob
}
}
BiConsumer
public class BiConsumerExample {
public static void main(String[] args) {
BiConsumer<String, Integer> printInfo =
(name, age) ->
System.out.println(name + " is " + age + " years old.");
printInfo.accept("Alice", 25);
printInfo.accept("Bob", 30);
}
}
BiConsumer with Map
Map<String, Integer> marks = new HashMap<>();
marks.put("Math", 90);
marks.put("Science", 85);
marks.put("English", 92);
BiConsumer<String, Integer> displayEntry =
(subject, score) ->
System.out.println(subject + " = " + score);
marks.forEach(displayEntry);
BiConsumer andThen()
BiConsumer<String, Integer> print =
(name, age) ->
System.out.println("Name: " + name + ", Age: " + age);
BiConsumer<String, Integer> greet =
(name, age) ->
System.out.println("Hello " + name +
"! You are " + age +
" years young.");
BiConsumer<String, Integer> combined =
print.andThen(greet);
combined.accept("Alice", 25);
Supplier
Supplier<Integer> randomSupplier =
() -> new Random().nextInt(100);
System.out.println(randomSupplier.get());
System.out.println(randomSupplier.get());
11. Java Built-in Functional Interfaces
| Interface | Purpose | Typical Method |
|---|---|---|
Function<T,R> | Accepts one value and returns a result. | apply() |
BiFunction<T,U,R> | Accepts two values and returns a result. | apply() |
Consumer<T> | Accepts one value and performs an action. | accept() |
BiConsumer<T,U> | Accepts two values and performs an action. | accept() |
Supplier<T> | Supplies a value without taking an input. | get() |
Predicate<T> | Tests one value and returns boolean. | test() |
BiPredicate<T,U> | Tests two values and returns boolean. | test() |
UnaryOperator<T> | Accepts and returns the same type. | apply() |
BinaryOperator<T> | Combines two values of the same type. | apply() |
UnaryOperator
public class UnaryOperatorExample {
public static void main(String[] args) {
UnaryOperator<String> toUpperCase =
str -> str.toUpperCase();
System.out.println(toUpperCase.apply("java")); // JAVA
System.out.println(toUpperCase.apply("function")); // FUNCTION
}
}
BinaryOperator
public class BinaryOperatorExample {
public static void main(String[] args) {
BinaryOperator<Integer> add = (a, b) -> a + b;
System.out.println(add.apply(10, 20)); // Output: 30
}
}
Stream reduce with BinaryOperator
List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);
BinaryOperator<Integer> sum = (a, b) -> a + b;
int result = numbers.stream().reduce(0, sum);
System.out.println("Sum: " + result); // Output: Sum: 15
12. Key Characteristics & Quick Revision
Static Methods in Interfaces
- Static methods belong to the interface itself, not to implementing objects.
- They are not inherited by implementing classes.
- They cannot be overridden.
- They are called directly using the interface name.
Default Methods in Interfaces
- Default methods contain an implementation inside the interface.
- They are inherited by implementing classes.
- An implementing class can override them.
- They allow interfaces to evolve without breaking existing implementations.
Quick Revision
| Concept | Key Point |
|---|---|
| Interface | Defines a contract/blueprint for implementing classes. |
| implements | Used by a class to implement an interface. |
| extends | Used by an interface to extend another interface. |
| Multiple inheritance | A class can implement multiple interfaces. |
| Normal interface | Regular interface containing method contracts plus supported default/static members. |
| Functional interface | Exactly one abstract method; works naturally with lambdas. |
| Marker interface | Empty interface used as a tag or marker. |
| Default method | Interface method with a body; can be inherited and overridden. |
| Static interface method | Belongs to the interface and is called using the interface name. |