CodeGym /행동 /JAVA 25 SELF /인터페이스의 static 메서드

인터페이스의 static 메서드

JAVA 25 SELF
레벨 21 , 레슨 3
사용 가능

1. 소개

Java 8 이전까지 인터페이스는 오로지 “계약”이었습니다. 즉, 추상 메서드만 있고 구현이나 로직은 전혀 없었죠. 하지만 Java 8부터 인터페이스는 조금 더 “자족적”이 되었습니다. 이제 default 메서드뿐 아니라 static 메서드도 포함할 수 있습니다.

인터페이스의 static 메서드는 구현 클래스가 아닌 인터페이스 자체에 속하는 메서드입니다. 객체를 생성할 필요가 없으며 인터페이스 이름으로 직접 호출합니다.

비유:
인터페이스의 static 메서드는 사무실 벽에 붙어 있는 안내서와 같습니다. 모든 직원(클래스)이 사용할 수 있지만, 안내서는 어느 특정 직원에게 속한 것이 아닙니다.

인터페이스의 static 메서드를 사용하면 해당 인터페이스와 연관된 보조 기능을 한데 묶으면서, 구현 클래스들의 네임스페이스를 어지럽히지 않을 수 있습니다.

인터페이스에서 static 메서드의 문법

static 메서드는 인터페이스 내부에서 static 키워드로 선언합니다. 구현(중괄호 안의 코드)을 가질 수 있으며, 인터페이스 이름으로만 호출됩니다.

예시:

public interface MathUtils {
    static int sum(int a, int b) {
        return a + b;
    }

    static double average(int a, int b) {
        return (a + b) / 2.0;
    }
}

인터페이스 static 메서드 호출:

int result = MathUtils.sum(5, 7);        // 12
double avg = MathUtils.average(10, 20);  // 15.0

주의:
인터페이스의 static 메서드는 구현 클래스나 그 객체를 통해 호출할 수 없습니다! 인터페이스 이름으로만 호출해야 합니다.

2. 인터페이스의 static 메서드와 default 메서드는 어떻게 다른가?

static 메서드:

  • 인터페이스 자체에 속한다.
  • 구현 클래스에 상속되지 않는다.
  • 객체를 통해 호출할 수 없다.
  • 구현 클래스에서 재정의할 수 없다.
  • 인터페이스 이름으로만 호출할 수 있다.

default 메서드:

  • 인터페이스를 구현한 클래스의 객체(인스턴스)에 속한다.
  • 구현 클래스에서 재정의할 수 있다.
  • 구현 클래스의 객체를 통해 호출할 수 있다.
  • 구현 클래스에 상속된다.

정리: default 메서드는 객체의 능력을 확장하고, static 메서드는 인터페이스 자체의 능력을 확장합니다.

비교 예시:

interface Printer {
    default void print(String text) {
        System.out.println("Default: " + text);
    }

    static void info() {
        System.out.println("Printer interface v1.0");
    }
}

class ConsolePrinter implements Printer {}

public class Main {
    public static void main(String[] args) {
        Printer.info(); // 인터페이스를 통해 static 메서드 호출

        ConsolePrinter cp = new ConsolePrinter();
        cp.print("Hello!"); // 객체를 통해 default 메서드 호출
        // cp.info(); // 오류! static 메서드는 객체로 호출할 수 없습니다
    }
}

3. 인터페이스에 static 메서드가 필요한 이유

과거에는 인터페이스와 관련된 유틸리티 기능을 추가하려면, Utils 또는 Helper 같은 접미사를 가진 별도의 클래스를 만들어야 했습니다:

public interface Movable {
    void move(int x, int y);
}

public class MovableUtils {
    public static void resetPosition(Movable m) {
        m.move(0, 0);
    }
}

이제는 이를 인터페이스 안에서 직접 할 수 있습니다:

public interface Movable {
    void move(int x, int y);

    static void resetPosition(Movable m) {
        m.move(0, 0);
    }
}

이렇게 하면 코드가 더 논리적이고 응집력 있게 됩니다. 인터페이스와 관련된 메서드가 이제 그 인터페이스 안에 함께 존재하기 때문입니다.

장점:

  • 인터페이스 계약 옆에 유틸리티 기능을 모아 둘 수 있다.
  • 구현 클래스의 네임스페이스를 ‘어지럽히지’ 않는다.
  • 가독성과 유지보수성이 향상된다.

4. 인터페이스 static 메서드의 제약과 특징

인터페이스의 static 메서드는 구현 클래스에 상속되지 않습니다.

구현 클래스의 객체나 클래스 이름으로는 호출할 수 없습니다. 오직 인터페이스 이름으로만 가능합니다!

인터페이스의 static 메서드는 항상 구현을 포함합니다. abstract나 default가 될 수 없습니다.

항상 구현을 포함합니다.

static 메서드는 인터페이스의 인스턴스 메서드나 변수에 접근할 수 없습니다.

오직 다른 static 멤버(예: static final 상수)만 접근할 수 있습니다.

인터페이스의 static 메서드는 private일 수도 있습니다(Java 9+).

인터페이스 내부에서만 사용할 보조용 private static 메서드를 만들 수 있습니다.

5. 예제: Movable 인터페이스의 static 메서드

Movable 인터페이스에 static 메서드를 어떻게 추가하는지 살펴봅시다. Movable 인터페이스는 여러 클래스(예: 로봇, 동물, 운송수단)가 구현한다고 가정합니다.

1단계. static 메서드를 포함한 인터페이스 선언:

public interface Movable {
    void move(int x, int y);

    static void resetPosition(Movable obj) {
        obj.move(0, 0);
    }

    static double distance(int x1, int y1, int x2, int y2) {
        int dx = x2 - x1;
        int dy = y2 - y1;
        return Math.sqrt(dx * dx + dy * dy);
    }
}

2단계. 클래스로 인터페이스 구현:

public class Robot implements Movable {
    private int x, y;

    public Robot(int x, int y) {
        this.x = x;
        this.y = y;
    }

    @Override
    public void move(int x, int y) {
        System.out.println("로봇이 점 (" + x + "," + y + ")으로 이동합니다");
        this.x = x;
        this.y = y;
    }

    public void printPosition() {
        System.out.println("현재 위치: (" + x + "," + y + ")");
    }
}

3단계. 인터페이스의 static 메서드 사용:

public class Main {
    public static void main(String[] args) {
        Robot robby = new Robot(10, 15);
        robby.printPosition();

        // 인터페이스 static 메서드로 위치 초기화
        Movable.resetPosition(robby);
        robby.printPosition();

        // 인터페이스 static 메서드로 두 점 사이 거리 계산
        double dist = Movable.distance(0, 0, 10, 15);
        System.out.println("거리: " + dist);
    }
}

결과:

현재 위치: (10,15)
로봇이 점 (0,0)으로 이동합니다
현재 위치: (0,0)
거리: 18.027756377319946

참고:
우리는 Movable.resetPosition(robby)를 호출하며, robby.resetPosition()가 아닙니다. static 메서드는 특정 객체가 아니라 인터페이스에 논리적으로 속하는 연산에 적합합니다.

6. 인터페이스의 private static 메서드

때로는 인터페이스 내부 용도의 보조 메서드가 필요합니다(예: 여러 static 또는 default 메서드에서 중복 코드를 없애기 위해). Java 9부터 인터페이스는 private static 메서드를 지원합니다.

예시:

public interface Logger {
    static void logInfo(String message) {
        log("INFO", message);
    }
    static void logError(String message) {
        log("ERROR", message);
    }
    private static void log(String level, String message) {
        System.out.println("[" + level + "] " + message);
    }
}

이제 log()는 인터페이스 밖에서는 접근할 수 없고, 다른 static 메서드 내부에서만 사용됩니다.

7. 표준 Java 라이브러리에서의 static 메서드

인터페이스의 static 메서드는 표준 Java 라이브러리에서 활발히 사용되며, 특히 컬렉션과 함수형 인터페이스에서 자주 볼 수 있습니다.

예시:

  • Comparator.comparing(), Comparator.reverseOrder()Comparator 인터페이스의 static 메서드.
  • Predicate.isEqual()Predicate 인터페이스의 static 메서드.
  • List.of(), Set.of(), Map.of() (Java 9+) — 불변 컬렉션을 만드는 static 메서드.

Comparator 예시:

import java.util.Comparator;

public class Main {
    public static void main(String[] args) {
        Comparator<String> cmp = Comparator.reverseOrder();
        int res = cmp.compare("a", "b"); // 양수: 역순이므로 "a" > "b"
        System.out.println(res);
    }
}

8. 인터페이스 static 메서드에서 자주 하는 실수

실수 №1: 구현 클래스의 객체로 static 메서드를 호출하려고 함.
이렇게는 동작하지 않습니다! 인터페이스의 static 메서드는 인터페이스 이름으로만 호출합니다. 예를 들어 Movable.resetPosition(obj)처럼 호출해야 하며, obj.resetPosition()가 아닙니다.

실수 №2: 구현 클래스에서 인터페이스의 static 메서드를 재정의하려고 함.
static 메서드는 상속되지도, 재정의되지도 않습니다! 클래스에 같은 이름의 static 메서드를 선언하더라도, 이는 인터페이스와 무관한 전혀 다른 메서드입니다.

실수 №3: static 메서드는 인스턴스 멤버에 접근할 수 없다는 점을 잊음.
인터페이스의 static 메서드는 static 멤버(예: static final 상수)만 사용할 수 있으며, 인스턴스 메서드나 변수에는 접근할 수 없습니다.

실수 №4: default 메서드와의 혼동.
default 메서드는 객체로 호출하고, static은 인터페이스 이름으로만 호출합니다. 혼동하지 마세요!

코멘트
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION