Introduction
Defining an interface involves declaring the contract that implementing classes must follow.
Basic Syntax
[access_modifier] interface InterfaceName {
// Constant declarations
// Method declarations
}
Simple Interface Definition
interface Printable {
void print(); // Abstract method
}
class Document implements Printable {
@Override
public void print() {
System.out.println("Printing document");
}
}
public class Main {
public static void main(String[] args) {
Printable doc = new Document();
doc.print();
}
}
Interface with Multiple Methods
interface Vehicle {
void start();
void stop();
void accelerate();
void brake();
}
class Car implements Vehicle {
@Override
public void start() {
System.out.println("Car starting");
}
@Override
public void stop() {
System.out.println("Car stopping");
}
@Override
public void accelerate() {
System.out.println("Car accelerating");
}
@Override
public void brake() {
System.out.println("Car braking");
}
}
public class Main {
public static void main(String[] args) {
Vehicle car = new Car();
car.start();
car.accelerate();
car.brake();
car.stop();
}
}
Interface with Constants
interface MathConstants {
// public static final (automatically)
double PI = 3.14159;
double E = 2.71828;
int MAX_SIZE = 100;
}
class Calculator implements MathConstants {
double circleArea(double radius) {
return PI * radius * radius; // Using constant
}
double exponential(double x) {
return Math.pow(E, x);
}
}
public class Main {
public static void main(String[] args) {
Calculator calc = new Calculator();
System.out.println("Area: " + calc.circleArea(5));
System.out.println("PI value: " + MathConstants.PI);
}
}
Method Signatures
interface Shape {
// Method with no parameters
void draw();
// Method with parameters
void resize(double scale);
// Method with return type
double area();
// Method with multiple parameters and return
String getInfo(String format, boolean detailed);
}
Complete Example: Bank Account
interface BankAccount {
// Constants
double MIN_BALANCE = 1000.0;
double INTEREST_RATE = 4.5;
// Abstract methods
void deposit(double amount);
boolean withdraw(double amount);
double getBalance();
void displayAccountInfo();
}
class SavingsAccount implements BankAccount {
private String accountNumber;
private String holderName;
private double balance;
SavingsAccount(String accountNumber, String holderName, double initialBalance) {
this.accountNumber = accountNumber;
this.holderName = holderName;
this.balance = initialBalance;
}
@Override
public void deposit(double amount) {
balance += amount;
System.out.println("Deposited: $" + amount);
}
@Override
public boolean withdraw(double amount) {
if (balance - amount >= MIN_BALANCE) {
balance -= amount;
System.out.println("Withdrawn: $" + amount);
return true;
}
System.out.println("Cannot withdraw. Minimum balance required: $" + MIN_BALANCE);
return false;
}
@Override
public double getBalance() {
return balance;
}
@Override
public void displayAccountInfo() {
System.out.println("Account: " + accountNumber);
System.out.println("Holder: " + holderName);
System.out.println("Balance: $" + balance);
System.out.println("Interest Rate: " + INTEREST_RATE + "%");
}
}
public class Main {
public static void main(String[] args) {
BankAccount account = new SavingsAccount("SA001", "John", 5000);
account.displayAccountInfo();
System.out.println();
account.deposit(2000);
account.withdraw(1500);
account.withdraw(5000); // Will fail - min balance
System.out.println("\nFinal balance: $" + account.getBalance());
}
}
Access Modifiers
// Public interface (can be accessed from anywhere)
public interface PublicInterface {
void method1();
}
// Default (package-private) interface
interface DefaultInterface {
void method2();
}
// Note: Interfaces cannot be private or protected
Interface Naming Convention
// Adjective ending in -able
interface Comparable { }
interface Serializable { }
interface Cloneable { }
interface Runnable { }
// Noun
interface List { }
interface Set { }
interface Map { }
// Action (verb)
interface ActionListener { }
interface Observer { }
Generic Interface
interface Container<T> {
void add(T item);
T get(int index);
int size();
}
class Box<T> implements Container<T> {
private T[] items;
private int count;
@SuppressWarnings("unchecked")
Box(int capacity) {
items = (T[]) new Object[capacity];
count = 0;
}
@Override
public void add(T item) {
items[count++] = item;
}
@Override
public T get(int index) {
return items[index];
}
@Override
public int size() {
return count;
}
}
public class Main {
public static void main(String[] args) {
Container<String> box = new Box<>(5);
box.add("Apple");
box.add("Banana");
System.out.println(box.get(0)); // Apple
System.out.println("Size: " + box.size());
}
}
Interface Extending Multiple Interfaces
interface Readable {
String read();
}
interface Writable {
void write(String data);
}
// Interface extending multiple interfaces
interface ReadWrite extends Readable, Writable {
void close();
}
class File implements ReadWrite {
private String content = "";
@Override
public String read() {
return content;
}
@Override
public void write(String data) {
content += data;
}
@Override
public void close() {
System.out.println("File closed");
}
}
public class Main {
public static void main(String[] args) {
ReadWrite file = new File();
file.write("Hello World");
System.out.println(file.read());
file.close();
}
}
Real-World Example: Logger Interface
interface Logger {
// Log levels as constants
int DEBUG = 1;
int INFO = 2;
int WARNING = 3;
int ERROR = 4;
// Methods
void log(String message, int level);
void setLogLevel(int level);
}
class ConsoleLogger implements Logger {
private int currentLevel = INFO;
@Override
public void log(String message, int level) {
if (level >= currentLevel) {
String levelName = getLevelName(level);
System.out.println("[" + levelName + "] " + message);
}
}
@Override
public void setLogLevel(int level) {
this.currentLevel = level;
}
private String getLevelName(int level) {
switch (level) {
case DEBUG: return "DEBUG";
case INFO: return "INFO";
case WARNING: return "WARNING";
case ERROR: return "ERROR";
default: return "UNKNOWN";
}
}
}
class FileLogger implements Logger {
private int currentLevel = INFO;
@Override
public void log(String message, int level) {
if (level >= currentLevel) {
String levelName = getLevelName(level);
System.out.println("Writing to file: [" + levelName + "] " + message);
}
}
@Override
public void setLogLevel(int level) {
this.currentLevel = level;
}
private String getLevelName(int level) {
switch (level) {
case DEBUG: return "DEBUG";
case INFO: return "INFO";
case WARNING: return "WARNING";
case ERROR: return "ERROR";
default: return "UNKNOWN";
}
}
}
public class Main {
public static void main(String[] args) {
Logger console = new ConsoleLogger();
console.log("Application started", Logger.INFO);
console.log("Debug message", Logger.DEBUG); // Won't show (level too low)
console.log("Warning occurred", Logger.WARNING);
console.log("Error occurred", Logger.ERROR);
System.out.println();
Logger file = new FileLogger();
file.setLogLevel(Logger.DEBUG);
file.log("Debug info", Logger.DEBUG); // Will show now
file.log("Error in file operation", Logger.ERROR);
}
}
Functional Interface
Interface with exactly one abstract method.
@FunctionalInterface
interface Calculator {
int calculate(int a, int b); // Single abstract method
}
class Addition implements Calculator {
@Override
public int calculate(int a, int b) {
return a + b;
}
}
public class Main {
public static void main(String[] args) {
Calculator add = new Addition();
System.out.println(add.calculate(10, 20)); // 30
// Can also use lambda (covered later)
Calculator multiply = (a, b) -> a * b;
System.out.println(multiply.calculate(5, 6)); // 30
}
}
Documentation
/**
* Interface for shape operations
* Provides methods for calculating area and perimeter
*
* @author Your Name
* @version 1.0
*/
interface Shape {
/**
* Calculates the area of the shape
* @return the area in square units
*/
double area();
/**
* Calculates the perimeter of the shape
* @return the perimeter in units
*/
double perimeter();
}
Best Practices
- Name interfaces with adjectives (-able suffix) or nouns
- Keep interfaces small (focused on one responsibility)
- Use constants for related values
- Document thoroughly with JavaDoc
- Make methods cohesive (related functionality)
- Don’t add unnecessary methods
Common Patterns
1. Strategy Pattern:
interface SortStrategy {
void sort(int[] array);
}
2. Observer Pattern:
interface Observer {
void update(String message);
}
3. Factory Pattern:
interface Product {
void create();
}
Quick Reference
// Basic definition
interface MyInterface {
// Constant
int CONSTANT = 100;
// Abstract method
void method1();
// Method with parameters
void method2(String param);
// Method with return type
String method3();
}
// Generic interface
interface Generic<T> {
void add(T item);
T get();
}
// Extending interfaces
interface Child extends Parent1, Parent2 {
void childMethod();
}
// Functional interface
@FunctionalInterface
interface Function {
int apply(int x);
}
Exam Tips
Remember:
- interface keyword to define
- All methods implicitly public abstract
- All variables implicitly public static final
- No method bodies (until Java 8 default methods)
- Constants in UPPER_CASE
- Can extend multiple interfaces
- Use implements keyword in class
- @FunctionalInterface for single method
- Generic interfaces use type parameters
- Document with JavaDoc comments
Common Questions:
- How to define interface?
- What are interface variables?
- Can interface methods have body?
- How to define constants in interface?
- What is functional interface?
- Can interface extend multiple interfaces?
- Naming conventions for interfaces?
- What is @FunctionalInterface?
- How to create generic interface?
- Best practices for defining interfaces?