Interface
Bài trước: Abstraction (Trừu tượng) — Đã học cách sử dụng abstract class để ẩn chi tiết triển khai. Bài này sẽ học về interface - contract định nghĩa hành vi mà class phải implement, cho phép đạt được 100% abstraction.
Sau bài này, bạn sẽ:
- Hiểu interface là contract định nghĩa "what" mà không phải "how"
- Tạo và implement interface với abstract, default, static, và private methods
- Sử dụng multiple interface implementation để đạt được tính linh hoạt
- Phân biệt interface vs abstract class và biết khi nào dùng loại nào
- Áp dụng functional interface với lambda expressions (Java 8+)
Interface là gì?
Interface là một contract (hợp đồng) hoặc protocol (giao thức) định nghĩa những gì một class phải làm, nhưng không định nghĩa cách làm như thế nào.
Contract Concept
// Contract: Bất kỳ class nào implement Flyable phải có khả năng bay
interface Flyable {
void fly();
void land();
}
// Bird ký hợp đồng - phải implement fly() và land()
class Bird implements Flyable {
@Override
public void fly() {
System.out.println("Bird flies by flapping wings");
}
@Override
public void land() {
System.out.println("Bird lands on tree");
}
}
// Airplane cũng ký hợp đồng - phải implement fly() và land()
class Airplane implements Flyable {
@Override
public void fly() {
System.out.println("Airplane flies with jet engines");
}
@Override
public void land() {
System.out.println("Airplane lands on runway");
}
}
- 100% abstraction: Hoàn toàn tách interface khỏi implementation
- Multiple inheritance: Class có thể implement nhiều interface
- Loose coupling: Giảm sự phụ thuộc giữa các module
- Contract-based programming: Đảm bảo class tuân theo contract
- Polymorphism: Xử lý các objects khác nhau qua chung một interface
Cú pháp: implements keyword
// Định nghĩa interface
interface Animal {
// Methods mặc định là public abstract
void eat();
void sleep();
}
// Implement interface
class Dog implements Animal {
@Override
public void eat() {
System.out.println("Dog eats bones");
}
@Override
public void sleep() {
System.out.println("Dog sleeps in doghouse");
}
}
// Sử dụng
public class Test {
public static void main(String[] args) {
Animal animal = new Dog(); // Polymorphism
animal.eat(); // Dog eats bones
animal.sleep(); // Dog sleeps in doghouse
}
}
Đặc điểm của Interface (Java 7 và trước)
Trước Java 8, interface có những đặc điểm:
| Đặc điểm | Mô tả |
|---|---|
| Methods | Tất cả methods đều là public abstract (mặc định) |
| Fields | Tất cả fields đều là public static final (constants) |
| Constructors | ❌ Không có constructor |
| Instantiation | ❌ Không thể khởi tạo trực tiếp |
| Implementation | Class phải implement tất cả methods |
interface Drawable {
// public static final - constants
int MAX_SIZE = 100;
String DEFAULT_COLOR = "Black";
// public abstract - methods
void draw();
void resize(int size);
}
class Circle implements Drawable {
@Override
public void draw() {
System.out.println("Drawing circle with color: " + DEFAULT_COLOR);
}
@Override
public void resize(int size) {
if (size <= MAX_SIZE) {
System.out.println("Resizing to: " + size);
}
}
}
Default Methods (Java 8+)
Java 8 cho phép interface có default methods với implementation:
interface Vehicle {
// Abstract method
void start();
// Default method - có implementation
default void honk() {
System.out.println("Beep! Beep!");
}
default void displayInfo() {
System.out.println("This is a vehicle");
}
}
class Car implements Vehicle {
@Override
public void start() {
System.out.println("Car engine starting...");
}
// Có thể override default method nếu muốn
@Override
public void honk() {
System.out.println("Car horn: HONK HONK!");
}
// Không override displayInfo() - dùng default implementation
}
class Motorcycle implements Vehicle {
@Override
public void start() {
System.out.println("Motorcycle engine starting...");
}
// Sử dụng default honk() và displayInfo()
}
- Backward compatibility: Thêm methods mới vào interface mà không phá vỡ code cũ
- Code reuse: Chia sẻ implementation giữa các implementing classes
- Optional implementation: Classes có thể chọn override hoặc dùng default
Static Methods trong Interface (Java 8+)
Interface có thể có static methods:
interface MathOperation {
// Abstract method
int calculate(int a, int b);
// Static method - gọi qua tên interface
static int add(int a, int b) {
return a + b;
}
static int multiply(int a, int b) {
return a * b;
}
static void printInfo() {
System.out.println("MathOperation interface");
}
}
class Addition implements MathOperation {
@Override
public int calculate(int a, int b) {
return MathOperation.add(a, b); // Gọi static method
}
}
public class Test {
public static void main(String[] args) {
// Gọi static method qua tên interface
int result = MathOperation.add(10, 20);
System.out.println("Result: " + result); // 30
MathOperation.printInfo();
}
}
Private Methods trong Interface (Java 9+)
Java 9 cho phép private methods để tái sử dụng code trong interface:
interface Logger {
// Public abstract method
void logInfo(String message);
void logWarning(String message);
void logError(String message);
// Default methods sử dụng private methods
default void logInfoWithTimestamp(String message) {
String formatted = formatMessage("INFO", message);
System.out.println(formatted);
}
default void logErrorWithTimestamp(String message) {
String formatted = formatMessage("ERROR", message);
System.err.println(formatted);
}
// Private method - code reuse
private String formatMessage(String level, String message) {
return "[" + java.time.LocalDateTime.now() + "] " +
"[" + level + "] " + message;
}
// Private static method
private static String getCurrentTime() {
return java.time.LocalDateTime.now().toString();
}
}
- Tái sử dụng code trong default và static methods
- Không thể gọi từ implementing classes
- Giúp giảm code duplication trong interface
Multiple Interface Implementation
Class có thể implement nhiều interface cùng lúc:
interface Swimmable {
void swim();
}
interface Flyable {
void fly();
}
interface Walkable {
void walk();
}
// Duck có thể walk, swim, và fly
class Duck implements Walkable, Swimmable, Flyable {
@Override
public void walk() {
System.out.println("Duck walks on land");
}
@Override
public void swim() {
System.out.println("Duck swims in water");
}
@Override
public void fly() {
System.out.println("Duck flies in sky");
}
}
// Fish chỉ có thể swim
class Fish implements Swimmable {
@Override
public void swim() {
System.out.println("Fish swims underwater");
}
}
// Airplane chỉ có thể fly
class Airplane implements Flyable {
@Override
public void fly() {
System.out.println("Airplane flies with engines");
}
}
// Demo
public class MultipleInterfaceDemo {
public static void main(String[] args) {
Duck duck = new Duck();
duck.walk(); // Duck walks on land
duck.swim(); // Duck swims in water
duck.fly(); // Duck flies in sky
// Polymorphism
Swimmable swimmer = duck;
swimmer.swim(); // Duck swims in water
}
}
Interface vs Abstract Class
| Đặc điểm | Interface | Abstract Class |
|---|---|---|
| Multiple inheritance | ✅ Có thể implement nhiều interface | ❌ Chỉ extend 1 class |
| Methods | Abstract (default) + default + static | Abstract + concrete |
| Fields | public static final only | Bất kỳ loại nào |
| Constructor | ❌ Không có | ✅ Có |
| Access modifiers | public (default) | Tất cả (public, protected, private) |
| Implementation | 100% abstract (Java 7), có default methods (Java 8+) | Có thể có concrete methods |
| Keyword | implements | extends |
| Khi nào dùng | Contract, behavior | IS-A relationship, shared code |
So sánh code
// Interface - pure contract
interface Playable {
void play();
void pause();
void stop();
// Java 8+
default void displayStatus() {
System.out.println("Status: Playing");
}
}
// Abstract class - có shared implementation
abstract class MediaPlayer {
protected String fileName;
protected boolean isPlaying;
public MediaPlayer(String fileName) {
this.fileName = fileName;
}
// Concrete method - shared code
public void loadFile() {
System.out.println("Loading file: " + fileName);
}
// Abstract method - subclass implement
public abstract void play();
// Concrete method
public void displayInfo() {
System.out.println("File: " + fileName);
System.out.println("Playing: " + isPlaying);
}
}
// Class có thể extend abstract class VÀ implement interface
class MP3Player extends MediaPlayer implements Playable {
public MP3Player(String fileName) {
super(fileName);
}
@Override
public void play() {
isPlaying = true;
System.out.println("Playing MP3: " + fileName);
}
@Override
public void pause() {
System.out.println("Paused");
}
@Override
public void stop() {
isPlaying = false;
System.out.println("Stopped");
}
}
Khi nào dùng Interface vs Abstract Class?
Dùng Interface khi:
- Định nghĩa contract/capability không liên quan đến class hierarchy
- Muốn multiple inheritance
- Các classes không liên quan cần share behavior (Flyable cho Bird và Airplane)
- Muốn đạt loose coupling
Dùng Abstract Class khi:
- Có IS-A relationship rõ ràng
- Muốn chia sẻ code (fields, concrete methods)
- Cần constructor hoặc state (instance variables)
- Các subclasses có nhiều điểm chung
Diamond Problem — Conflict Resolution cho Default Methods
Diamond Problem xảy ra khi class implement 2 interface có cùng default method. Java giải quyết bằng conflict resolution rules.
Diamond Problem — Step-by-step
Hãy đi từng bước để hiểu tại sao có conflict:
Bước 1: Interface A và B đều có default method cùng tên
Bước 2: Class C implement cả A và B — compiler thấy 2 "lựa chọn" cho cùng 1 method
Bước 3: Compiler không tự ý chọn — bắt bạn phải quyết định tường minh
interface A {
default void method() {
System.out.println("A's method");
}
}
interface B {
default void method() {
System.out.println("B's method");
}
}
// ❌ Compile error! Ambiguous method
class C implements A, B {
// Compiler không biết dùng A.method() hay B.method()
}
Resolution Rules
Java yêu cầu class override method tường minh để giải quyết conflict:
class C implements A, B {
@Override
public void method() {
// Rule 1: Class method thắng interface default method
System.out.println("C's method");
}
}
Gọi specific interface method
class C implements A, B {
@Override
public void method() {
// Gọi method từ interface A
A.super.method(); // Output: A's method
// Hoặc gọi từ interface B
B.super.method(); // Output: B's method
// Hoặc custom logic
System.out.println("C's custom method");
}
}
Diamond Problem với Interface Hierarchy
interface Top {
default void method() {
System.out.println("Top's method");
}
}
interface Left extends Top {
// Không override - dùng Top's method
}
interface Right extends Top {
@Override
default void method() {
System.out.println("Right's method");
}
}
class Bottom implements Left, Right {
// ❌ Compile error! Conflict giữa Top.method() (qua Left) và Right.method()
@Override
public void method() {
// Phải override để resolve
Right.super.method(); // Output: Right's method
}
}
Conflict Resolution Priority
1. Class method > Interface default method
2. Most specific interface wins (subinterface > superinterface)
3. Ambiguity → Compile error → Phải override tường minh
interface A {
default void method() {
System.out.println("A");
}
}
interface B extends A {
@Override
default void method() {
System.out.println("B");
}
}
class C implements A, B {
// ✅ Không cần override - B most specific, thắng A
// method() sẽ gọi B.method()
}
public class DiamondDemo {
public static void main(String[] args) {
C c = new C();
c.method(); // Output: B (B more specific than A)
}
}
Khi class inherit nhiều default method có cùng signature, compiler áp dụng most specific override rule. Nếu không xác định được most specific → compile error.
Giống tranh chấp quyền thừa kế: Nếu 2 người cùng claim tài sản (default method), phải có quyết định rõ ràng (override) hoặc người gần nhất thắng (most specific).
Interface Evolution: Java 8 → 9 → 14+
| Java Version | Feature | Mục đích |
|---|---|---|
| Java 7- | Abstract methods, constants | 100% abstraction |
| Java 8 | Default methods, static methods | Backward compatibility, utility methods |
| Java 9 | Private methods | Code reuse trong default methods |
| Java 14+ | Records (special interface usage) | Immutable data carriers |
Default Methods (Java 8)
Cho phép thêm methods vào interface mà không phá vỡ code cũ:
interface OldInterface {
void oldMethod();
}
// Java 8: Thêm method mới
interface OldInterface {
void oldMethod();
// Default method - existing implementations không cần sửa
default void newMethod() {
System.out.println("Default implementation");
}
}
// Existing class vẫn compile OK
class OldClass implements OldInterface {
@Override
public void oldMethod() {
// implementation
}
// Không cần implement newMethod() - dùng default
}
Static Methods (Java 8)
Utility methods trong interface:
interface StringUtils {
// Static method - gọi qua tên interface
static boolean isEmpty(String s) {
return s == null || s.isEmpty();
}
static String reverse(String s) {
return new StringBuilder(s).reverse().toString();
}
}
// Usage
System.out.println(StringUtils.isEmpty("")); // true
System.out.println(StringUtils.reverse("hello")); // olleh
Private Methods (Java 9)
Tái sử dụng code trong default và static methods:
interface Logger {
default void logInfo(String message) {
log("INFO", message);
}
default void logError(String message) {
log("ERROR", message);
}
// Private method - code reuse
private void log(String level, String message) {
System.out.println("[" + level + "] " + getCurrentTime() + ": " + message);
}
// Private static method
private static String getCurrentTime() {
return java.time.LocalDateTime.now().toString();
}
}
Sealed Interfaces (Java 17+)
Tương tự sealed classes, sealed interfaces kiểm soát implementations.
public sealed interface Payment
permits CreditCardPayment, PayPalPayment, CryptoPayment {
void processPayment(double amount);
}
public final class CreditCardPayment implements Payment {
@Override
public void processPayment(double amount) {
System.out.println("Processing credit card: $" + amount);
}
}
public final class PayPalPayment implements Payment {
@Override
public void processPayment(double amount) {
System.out.println("Processing PayPal: $" + amount);
}
}
public non-sealed class CryptoPayment implements Payment {
// non-sealed → cho phép subclasses khác implement
@Override
public void processPayment(double amount) {
System.out.println("Processing crypto: $" + amount);
}
}
// ❌ Compile error - BankTransfer không được phép
// public class BankTransferPayment implements Payment { }
Exhaustive Switch với Sealed Interface
public void process(Payment payment) {
// Compiler biết tất cả implementations
String result = switch (payment) {
case CreditCardPayment cc -> "Credit card";
case PayPalPayment pp -> "PayPal";
case CryptoPayment cp -> "Crypto";
// Không cần default - compiler đảm bảo exhaustiveness
};
System.out.println("Payment type: " + result);
}
Bẫy 1: Default Method Conflict Resolution
interface A {
default void method() { System.out.println("A"); }
}
interface B {
default void method() { System.out.println("B"); }
}
// ❌ Compile error! Phải override
class C implements A, B {
// Bắt buộc phải resolve conflict
}
// ✅ Đúng cách
class C implements A, B {
@Override
public void method() {
A.super.method(); // Chọn A
// hoặc B.super.method(); // Chọn B
// hoặc custom implementation
}
}
Bẫy 2: Interface Fields are public static final
interface Constants {
int MAX_SIZE = 100; // Tự động: public static final
// Tương đương với:
// public static final int MAX_SIZE = 100;
}
// ❌ Không thể thay đổi
Constants.MAX_SIZE = 200; // Compile error! final field
// ❌ Không thể có instance fields
interface Bad {
int value; // Compile error! Must be initialized (static final)
}
Bẫy 3: Interface methods are public (default)
interface MyInterface {
void method(); // Tự động: public abstract
// ❌ Compile error! Interface method cannot be protected
protected void badMethod();
// ❌ Compile error! Interface method cannot be private (trừ private helper methods Java 9+)
private void anotherBadMethod(); // Chỉ OK nếu có body (private helper)
// ✅ Private method phải có body (Java 9+)
private void helperMethod() {
System.out.println("Helper");
}
}
Bẫy 4: Override default method với access modifier hẹp hơn
interface A {
default void method() {
System.out.println("A");
}
}
class B implements A {
// ❌ Compile error! Cannot reduce visibility
@Override
protected void method() {
System.out.println("B");
}
// ✅ Phải là public
@Override
public void method() {
System.out.println("B");
}
}
Functional Interface
Functional Interface là interface chỉ có đúng 1 abstract method.
@FunctionalInterface // Optional annotation
interface Calculator {
// Chỉ 1 abstract method
int calculate(int a, int b);
// Có thể có default methods
default void printResult(int result) {
System.out.println("Result: " + result);
}
// Có thể có static methods
static void info() {
System.out.println("Calculator interface");
}
}
// Implement bằng class
class Addition implements Calculator {
@Override
public int calculate(int a, int b) {
return a + b;
}
}
// Implement bằng Lambda expression (Java 8+)
public class FunctionalInterfaceDemo {
public static void main(String[] args) {
// Lambda expression
Calculator addition = (a, b) -> a + b;
Calculator subtraction = (a, b) -> a - b;
Calculator multiplication = (a, b) -> a * b;
System.out.println(addition.calculate(10, 5)); // 15
System.out.println(subtraction.calculate(10, 5)); // 5
System.out.println(multiplication.calculate(10, 5)); // 50
}
}
- Không bắt buộc nhưng nên sử dụng
- Compiler sẽ báo lỗi nếu interface có nhiều hơn 1 abstract method
- Giúp code rõ ràng và dễ maintain
Built-in Functional Interfaces
Java cung cấp nhiều functional interfaces trong java.util.function:
import java.util.function.*;
public class BuiltInFunctionalInterfaces {
public static void main(String[] args) {
// Predicate<T> - test condition, return boolean
Predicate<Integer> isEven = n -> n % 2 == 0;
System.out.println(isEven.test(10)); // true
// Function<T, R> - transform T to R
Function<String, Integer> stringLength = s -> s.length();
System.out.println(stringLength.apply("Hello")); // 5
// Consumer<T> - consume T, return nothing
Consumer<String> printer = s -> System.out.println(s);
printer.accept("Hello World");
// Supplier<T> - supply T, no input
Supplier<Double> randomValue = () -> Math.random();
System.out.println(randomValue.get());
// BiFunction<T, U, R> - two inputs, one output
BiFunction<Integer, Integer, Integer> sum = (a, b) -> a + b;
System.out.println(sum.apply(5, 3)); // 8
}
}
Repeatable Annotations (Java 8+)
Từ Java 8, có thể dùng cùng annotation nhiều lần trên 1 element với @Repeatable:
import java.lang.annotation.*;
// Container annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Schedules {
Schedule[] value();
}
// Repeatable annotation
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Repeatable(Schedules.class) // Chỉ định container
public @interface Schedule {
String day();
String time();
}
// Usage
public class TaskScheduler {
@Schedule(day = "Monday", time = "9:00")
@Schedule(day = "Wednesday", time = "14:00")
@Schedule(day = "Friday", time = "16:00")
public void backupDatabase() {
System.out.println("Backing up database...");
}
public static void main(String[] args) throws Exception {
var method = TaskScheduler.class.getMethod("backupDatabase");
// Đọc repeatable annotations
Schedule[] schedules = method.getAnnotationsByType(Schedule.class);
for (Schedule schedule : schedules) {
System.out.println("Scheduled: " + schedule.day() + " at " + schedule.time());
}
}
}
Type Annotations (Java 8+)
Annotations có thể dùng trên types (không chỉ declarations):
import java.lang.annotation.*;
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE) // TYPE_USE cho phép dùng trên types
public @interface NonNull {
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE_USE)
public @interface Readonly {
}
public class TypeAnnotationDemo {
// Type annotations
public void process(@NonNull String name) {
// name không thể null
}
public @NonNull String getName() {
return "John";
}
// Generic type với annotation
public List<@NonNull String> getNames() {
return List.of("Alice", "Bob");
}
// Cast với annotation
public void test(Object obj) {
String s = (@NonNull String) obj;
}
// Array với annotation
public void array() {
@NonNull String[] names = new String[10];
String @NonNull [] alsoNames = new String[10]; // Array itself cannot be null
}
// Nested types
public Map<@NonNull String, @Readonly List<@NonNull String>> data;
}
Type annotations (với @Target(ElementType.TYPE_USE)) có thể đặt ở bất kỳ type use nào: parameters, return types, casts, generics, arrays, etc. Frameworks như Checker Framework sử dụng để static analysis.
Ví dụ thực tế
Ví dụ 1: Comparable Interface
class Student implements Comparable<Student> {
private String name;
private int age;
private double gpa;
public Student(String name, int age, double gpa) {
this.name = name;
this.age = age;
this.gpa = gpa;
}
// Implement Comparable - sort by GPA descending
@Override
public int compareTo(Student other) {
return Double.compare(other.gpa, this.gpa);
}
@Override
public String toString() {
return name + " (Age: " + age + ", GPA: " + gpa + ")";
}
public String getName() { return name; }
public int getAge() { return age; }
public double getGpa() { return gpa; }
}
// Sử dụng
import java.util.*;
public class ComparableDemo {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("An", 20, 3.5));
students.add(new Student("Binh", 21, 3.8));
students.add(new Student("Chi", 19, 3.9));
students.add(new Student("Dung", 20, 3.2));
System.out.println("Before sorting:");
students.forEach(System.out::println);
// Sort sử dụng compareTo()
Collections.sort(students);
System.out.println("\nAfter sorting by GPA:");
students.forEach(System.out::println);
}
}
Ví dụ 2: Serializable Interface
import java.io.*;
// Marker interface - không có methods
class Employee implements Serializable {
private static final long serialVersionUID = 1L;
private String name;
private int id;
private transient String password; // không serialize
public Employee(String name, int id, String password) {
this.name = name;
this.id = id;
this.password = password;
}
@Override
public String toString() {
return "Employee{name='" + name + "', id=" + id +
", password='" + password + "'}";
}
}
public class SerializableDemo {
public static void main(String[] args) {
Employee emp = new Employee("Nguyen Van A", 1001, "secret123");
// Serialize
try (ObjectOutputStream out = new ObjectOutputStream(
new FileOutputStream("employee.dat"))) {
out.writeObject(emp);
System.out.println("Serialized: " + emp);
} catch (IOException e) {
e.printStackTrace();
}
// Deserialize
try (ObjectInputStream in = new ObjectInputStream(
new FileInputStream("employee.dat"))) {
Employee loadedEmp = (Employee) in.readObject();
System.out.println("Deserialized: " + loadedEmp);
// password sẽ là null vì là transient
} catch (IOException | ClassNotFoundException e) {
e.printStackTrace();
}
}
}
Ví dụ 3: Custom Interface - Drawable
interface Drawable {
void draw();
default void resize(double factor) {
System.out.println("Resizing by factor: " + factor);
draw();
}
static void printInfo() {
System.out.println("Drawable interface v1.0");
}
}
interface Colorable {
void setColor(String color);
String getColor();
}
// Implement multiple interfaces
class Circle implements Drawable, Colorable {
private double radius;
private String color;
public Circle(double radius, String color) {
this.radius = radius;
this.color = color;
}
@Override
public void draw() {
System.out.println("Drawing a " + color + " circle with radius " + radius);
}
@Override
public void setColor(String color) {
this.color = color;
}
@Override
public String getColor() {
return color;
}
@Override
public void resize(double factor) {
radius *= factor;
Drawable.super.resize(factor); // Call default method
}
}
class Rectangle implements Drawable, Colorable {
private double width;
private double height;
private String color;
public Rectangle(double width, double height, String color) {
this.width = width;
this.height = height;
this.color = color;
}
@Override
public void draw() {
System.out.println("Drawing a " + color + " rectangle " +
width + "x" + height);
}
@Override
public void setColor(String color) {
this.color = color;
}
@Override
public String getColor() {
return color;
}
}
// Demo
public class DrawableDemo {
public static void main(String[] args) {
Drawable.printInfo();
Drawable[] shapes = {
new Circle(5, "Red"),
new Rectangle(4, 6, "Blue")
};
for (Drawable shape : shapes) {
shape.draw();
shape.resize(1.5);
System.out.println();
}
}
}
Ví dụ 4: Strategy Pattern với Interface
// Strategy interface
interface PaymentStrategy {
void pay(double amount);
}
// Concrete strategies
class CreditCardStrategy implements PaymentStrategy {
private String cardNumber;
public CreditCardStrategy(String cardNumber) {
this.cardNumber = cardNumber;
}
@Override
public void pay(double amount) {
System.out.println("Paid $" + amount + " using Credit Card " +
cardNumber.substring(12));
}
}
class PayPalStrategy implements PaymentStrategy {
private String email;
public PayPalStrategy(String email) {
this.email = email;
}
@Override
public void pay(double amount) {
System.out.println("Paid $" + amount + " using PayPal " + email);
}
}
class CashStrategy implements PaymentStrategy {
@Override
public void pay(double amount) {
System.out.println("Paid $" + amount + " in cash");
}
}
// Context
class ShoppingCart {
private PaymentStrategy paymentStrategy;
public void setPaymentStrategy(PaymentStrategy paymentStrategy) {
this.paymentStrategy = paymentStrategy;
}
public void checkout(double amount) {
if (paymentStrategy == null) {
System.out.println("Please select a payment method");
return;
}
paymentStrategy.pay(amount);
}
}
// Demo
public class StrategyPatternDemo {
public static void main(String[] args) {
ShoppingCart cart = new ShoppingCart();
// Pay with credit card
cart.setPaymentStrategy(new CreditCardStrategy("1234567812345678"));
cart.checkout(100.50);
// Pay with PayPal
cart.setPaymentStrategy(new PayPalStrategy("[email protected]"));
cart.checkout(75.00);
// Pay with cash
cart.setPaymentStrategy(new CashStrategy());
cart.checkout(50.25);
}
}
Functional Interface và Object Methods
Tại sao Comparator có method equals() mà vẫn là @FunctionalInterface?
Quy tắc: Object methods không tính vào SAM count
SAM = Single Abstract Method. Khi đếm abstract methods của functional interface, Java không tính các public methods kế thừa từ java.lang.Object (equals(), hashCode(), toString()).
Lý do: Mọi class đều extends Object → đã có implementation sẵn cho các methods này.
@FunctionalInterface
public interface Comparator<T> {
// Abstract method #1 — đây là SAM
int compare(T o1, T o2);
// Kế thừa từ Object — KHÔNG tính vào SAM count
boolean equals(Object obj);
// Nhiều default methods — không tính
// reversed(), thenComparing(), ...
}
// → Vẫn là @FunctionalInterface vì chỉ có 1 abstract method "thật"
Ví dụ: Tự tạo functional interface có equals()
@FunctionalInterface
interface Transformer<T> {
T transform(T input); // SAM — đếm
boolean equals(Object obj); // Object method — không đếm
// ✅ Vẫn là functional interface → dùng được lambda
}
// Sử dụng với lambda
Transformer<String> upper = s -> s.toUpperCase();
System.out.println(upper.transform("hello")); // HELLO
Câu hỏi: Interface sau có phải functional interface không?
interface MyInterface {
void doWork();
boolean equals(Object o);
String toString();
}
Đáp án: Có! equals() và toString() là public methods từ Object → không tính. Chỉ có doWork() là abstract method duy nhất.
Bài tập thực hành
Bài 1: Shape Interface
Tạo interface Shape với methods:
double getArea()double getPerimeter()default void displayInfo()
Implement cho: Circle, Rectangle, Triangle, Square
Bài 2: Sortable Interface
Tạo interface Sortable<T>:
void sort()default void reverse()
Implement cho IntArray, StringArray, StudentArray
Bài 3: Database Operations
Tạo interface hierarchy:
Readable:read()Writable:write()Deletable:delete()CRUDOperations extends Readable, Writable, Deletable:update()
Implement cho: FileDatabase, MySQLDatabase, MongoDBDatabase
Bài 4: Logger System
Tạo interface Logger:
void log(String message)default void logWithLevel(String level, String message)static Logger getLogger(String type)
Implement: ConsoleLogger, FileLogger, DatabaseLogger
Tổng kết
- Interface = Contract: Định nghĩa what, không phải how
- Multiple inheritance: Class implement nhiều interface
- 100% abstraction: Tách interface khỏi implementation
- Default methods (Java 8+): Có implementation trong interface
- Functional interface: 1 abstract method, dùng với lambda
- Marker interface: Không có methods (Serializable, Cloneable)
| Java Version | Features |
|---|---|
| Java 7- | Abstract methods, constants |
| Java 8 | Default methods, static methods |
| Java 9 | Private methods |
| Functional + Object methods | Object methods (equals, hashCode, toString) không tính vào SAM count |
Interface là công cụ mạnh mẽ giúp code flexible, maintainable, và loosely coupled!