본문 바로가기

Java

인터페이스(Interface)

인터페이스란

 

자바에서 인터페이스(interface)란 클래스가 구현해야 하는 메소드의 집합을 정의하는 특수한 타입이다.

 

이러한 인터페이스를 사용하는 이유는 다음과 같다.

  • 추상 메소드를 통해 객체들 간의 네이밍을 통일할 수 있고 이로 인해 소스의 가독성과 유지보수가 향상된다.
  • 확장에는 열려 있고 변경에는 닫혀 있는 객체 간 결합도(코드 종속성)를 낮춘 유연한 방식의 개발이 가능하다.

 

인터페이스는 클래스가 따라야 할 일종의 계약(규약)을 정의한다. 개발자는 인터페이스에 정의된 메소드를 구현한 클래스를 통해 실제 기능을 제공한다. 인터페이스는 메서드 이름, 매개변수 타입, 리턴 타입만 정의하고, 구현은 이를 구현하는 클래스에서 제공한다.

 

인터페이스를 구현한 클래스는 반드시 인터페이스에서 정의된 모든 메서드를 구현해야 한다. 이를 통해 클래스는 인터페이스의 계약을 준수하게 되며, 이러한 메소드들을 통해 객체는 상호작용할 수 있다. 다시 말해 인터페이스는 다양한 클래스가 동일한 방식으로 상호작용할 수 있도록 하는 일관성을 제공한다.

 


 

인터페이스 사용법

// 인터페이스 정의
interface Animal {
    void makeSound(); // 추상 메소드
}

// Dog 클래스가 Animal 인터페이스를 구현
class Dog implements Animal {
    @Override
    public void makeSound() {
        System.out.println("Bark");
    }
}

// Cat 클래스가 Animal 인터페이스를 구현
class Cat implements Animal {
    @Override
    public void makeSound() {
        System.out.println("Meow");
    }
}

// 메인 클래스
public class InterfaceExample {
    public static void main(String[] args) {
        Animal dog = new Dog(); 
        Animal cat = new Cat(); 
        
        dog.makeSound(); // "Bark" 출력
        cat.makeSound(); // "Meow" 출력
    }
}

 


 

인터페이스의 특징

  1. 인터페이스의 모든 필드는 public static final이며 이를 생략할 수 있다.
    - 보통 클래스에서 공통으로 사용되는 상수를 정의할 때 쓰인다.  
      ex) MAX_SIZE, DEFAULT_NAME

  2. 인터페이스의 모든 메소드는 public abstract이며 이를 생략할 수 있다.
    - java8 부터는 default, static 메소드도 사용할 수 있다.
    - java9 부터는 private, private static 메소드도 사용할 수 있다.

  3. 인터페이스는 상속 키워드로 implements를 사용한다.

  4. 다중 구현이 가능하다.
    - 하나의 클래스가 여러 개의 인터페이스를 구현하는 것이 가능

  5. 인터페이스가 인터페이스를 상속받는 것이 가능하다.
    - 이 경우에는 다중 상속도 가능하다.

 

[인터페이스가 인터페이스를 상속하는 경우]

public interface Animal {
    void eat();
    void sleep();
}

public interface Pet {
    void play();
}

public interface Dog extends Animal, Pet {
    void bark();
}

// Dog 인터페이스를 구현하는 클래스
public class Labrador implements Dog {
    @Override
    public void eat() {
        System.out.println("Labrador is eating.");
    }

    @Override
    public void sleep() {
        System.out.println("Labrador is sleeping.");
    }

    @Override
    public void play() {
        System.out.println("Labrador is playing.");
    }

    @Override
    public void bark() {
        System.out.println("Labrador is barking.");
    }
}

 


 

Java8의 default, static 메소드

 

java8 이후에 인터페이스에 default, static 메소드가 추가된 이유는 다음과 같다.
(참고로 default, static 메소드는 public이다.)

[주의]

* 자바의 접근제어자인 default와 인터페이스의 default 메소드는 엄연히 다르다.
* 접근 제어자인 default는 접근제어자를 생략하지만 인터페이스에서는 명시해야 한다.

(접근제어자 default)
default String name; (X) / String name; (O)

(인터페이스 default 메소드)
default int multiply(int a, int b)

 

 

 

* 인터페이스를 구현한 기존 클래스에 영향을 주지 않으면서 인터페이스 수정

 

추상 메소드는 선언만 할 뿐 로직을 가질 수 없다. 따라서 java 7 까지는 인터페이스에 로직을 구현하는 것이 불가능했다. 하지만 다음과 같은 상황을 가정하자. 계산기 기능을 정의하는 기존 인터페이스에 +,- 연산만 있었는데 이번에 * 연산이 추가되어야 하는 상황이다.

이럴 경우 인터페이스에 * 연산 추상 메소드를 정의하면 해당 메소드를 구현하는 모든 클래스에 영향이 미친다. 이런 경우에 인터페이스에 * 연산 로직을 default 또는 static 메소드로 추가하면 기존 클래스에 영향을 미치지 않는다.

// Java 7까지의 인터페이스
interface Calculator {
    int add(int a, int b);
    int subtract(int a, int b);
}

// Calculator 인터페이스를 구현하는 클래스
class BasicCalculator implements Calculator {
    @Override
    public int add(int a, int b) {
        return a + b;
    }

    @Override
    public int subtract(int a, int b) {
        return a - b;
    }
}

 

// Java 8 이후의 인터페이스
interface Calculator {
    int add(int a, int b);
    int subtract(int a, int b);

    // 새로 추가된 default 메서드
    default int multiply(int a, int b) {
        return a * b;
    }
}

// 기존 BasicCalculator 클래스는 변경하지 않아도 된다.
class BasicCalculator implements Calculator {
    @Override
    public int add(int a, int b) {
        return a + b;
    }

    @Override
    public int subtract(int a, int b) {
        return a - b;
    }
}

 

 

 

* 유틸리티성 로직 구현

 

static 메소드를 구현하면 다음과 같이 유틸리티성 로직을 구현할 수 있는 장점이 있다. 참고로 static 메소드는 override하는 것이 불가능하다.

public interface Validator<T> {
    // 추상 메소드
    boolean isValid(T t);

    // 공통 유틸리티 static 메소드
    static boolean isNonNull(Object obj) {
        return obj != null;
    }

    // 공통 유틸리티 static 메소드
    static boolean isNonEmptyString(String str) {
        return str != null && !str.isEmpty();
    }
}

 

public class UserValidator implements Validator<String> {
    @Override
    public boolean isValid(String username) {
        // 예: 사용자 이름이 null이 아니고 빈 문자열이 아니면 유효함
        return Validator.isNonNull(username) && Validator.isNonEmptyString(username);
    }
}

 

public class Main {
    public static void main(String[] args) {
        UserValidator userValidator = new UserValidator();
        System.out.println("Is valid username: " + userValidator.isValid("john_doe")); 
        System.out.println("Is valid username: " + userValidator.isValid(""));

        // static 메소드 직접 사용
        System.out.println("Is non-null: " + Validator.isNonNull("test")); 
        System.out.println("Is non-empty string: " + Validator.isNonEmptyString("test")); 
    }
}

 


 

Java9의 private, private static 메소드

 

Java9 이후에 인터페이스에 private, private static 메소드가 추가되었다. 그 이유는 default와 static 메소드가 public이라 외부에 노출되는 단점을 보완하기 위해서다.

 

public interface PaymentProcessor {
    void processPayment(double amount);

    // default 메서드
    default void refundPayment(double amount) {
        if (validateAmount(amount)) {
            System.out.println("Refunding payment of $" + amount);
        } else {
            System.out.println("Invalid refund amount: $" + amount);
        }
    }

    // static 메서드
    static void printPaymentInfo() {
        System.out.println("All payments are processed securely.");
    }

    // private 메서드
    private boolean validateAmount(double amount) {
        return amount > 0;
    }
}

 

 


출처 :
https://coding-factory.tistory.com/867
https://n1tjrgns.tistory.com/289
https://velog.io/@alkwen0996/%EC%9D%B8%ED%84%B0%ED%8E%98%EC%9D%B4%EC%8A%A4interface

 

'Java' 카테고리의 다른 글

SOLID 원칙  (0) 2024.07.13
추상 클래스와 인터페이스  (0) 2024.07.13
상속(Inheritance)  (0) 2024.07.12
동일성(identity)과 동등성(equality)  (1) 2024.06.07
깊은 복사(Deep Copy)와 얕은 복사(Shallow Copy)  (0) 2024.05.31