ABOUT ME

-

Today
-
Yesterday
-
Total
-
  • 인터페이스의 default, static 메소드
    Java & Kotlin 2021. 10. 6. 00:37
    반응형

    1) 서론

    창피하지만, 최근 이펙티브 자바를 읽으며 인터페이스에 default, static 메서드가 된다는 것을 알았습니다. JDK 17이 나오는 시점에서 JDK 8 조차 제대로 업데이트되지 않은 채 "그게 돼?" 라는 충격에 빠졌습니다.

     

    그래서 이번 기회에 해당 메서드들이 왜 만들어졌고, 어떻게 사용되는지 공부해봤습니다.

     


    2) default / static 메서드

    원래 인터페이스는 추상 메서드만 사용 가능했었습니다. 여기서 추상메서드구현부가 없는, 아직 완성되지 않은 메서드인데요. 

     

    왜 추상메서드만 허용을 했었는가에 대한 명확한 설명은 없지만, 오라클 공식문서를 참고하면 오라클이 인터페이스를 어떻게 바라보고 있는지 조금은 알 수 있습니다. 

     

    오라클 공식문서에서는 인터페이스를 자동차 산업의 표준이 되는 설명서로서 바라보고 있습니다. 즉 자동차를 만들 때 자신들의 어떤 메서드를 사용해서, 운영하고 제조할지 표준으로서 설명서처럼 만들어 놓은 것입니다. 

     

    하지만 문제는 이 표준을 만든 오라클이 자동차가 날 수 있는 기능을 추가로 넣으면 어떻게 될까요? 이를 구현한 각각의 자동차 회사들은 자신들의 스타일대로 해당 표준을 따라야 할 것입니다. 필요 여부에 관계없이 강제로요. 

     

    이것을 자바로 가지고 오겠습니다.

     

    오라클이 만든 다양한 인터페이스가 존재합니다. 그리고 해당 인터페이스를 구현한 다양한 API들이 있습니다.

     

    이때 오라클이 인터페이스 하나의 추상 메서드를 추가하더라도, 이를 구현하는 엄청난 수의 API들이 자신들의 상황에 맞게 구현해야 합니다. 문제는 인터페이스에서 추상메서드는 구현을 강제하고 있습니다. 구현하고 있는 API들은 필요 여부와 관계없이 강제로 구현해야합니다. 

     

    그래서 이러한 문제점을 유연하게 해결하고자 오라클은 default, static 메서드를 인터페이스에 허용하게 됐습니다. 


    3) default method

    // Test 인터페이스
    public interface Test {
        void testMethod();
    }
    
    
    // Test 구현한 TestImipl 클래스
    public class TestImpl implements Test {
    
        @Override
        public void testMethod() {
            System.out.println("=== testMethod1 ===");
        }
        
    }

    위는 기본적인 형태의 인터페이스와 구현한 클래스입니다. 

     

    당연히 TestImpl 클래스는 testMethod()의 오버라이딩이 강제됩니다. 

     

    이때 Test 인터페이스에 새로운 추상 메서드가 들어오면 어떻게 될까요? 당연히 오버라이딩이 강제되며, 사용하지 않더라도 일단 구현해야 합니다. 지금은 출력만 하는 간단한 구현부지만, 복잡한 API의 경우에는 불필요한 시간낭비와 노력이 필요합니다.

     

    // Test 인터페이스
    public interface Test {
    
        void testMethod();
    
        // 추가된 default 메서드
        default void defaultMethod() {
            System.out.println("defaultMethod");
        }
        
    }
    
    
    // Test 구현한 TestImipl 클래스
    public class TestImpl implements Test {
    
        @Override
        public void testMethod() {
            System.out.println("=== testMethod1 ===");
        }
        
        // default 메소드, 강제 구현 X
        @Override
        public void defaultMethod() {
            Test.super.defaultMethod();
        }
        
    }

    그래서 Test 인터페이스에 default 메서드를 구현합니다.

     

    특징은 추상메서드가 아니기 때문에 인터페이스에서 본문이 구현되어야 합니다. 그리고 이를 구현한 TestImpl 클래스에서는 강제로 defaultMethod()를 구현하지 않아도 됩니다. 


    4) static method

    오라클 공식문서에서는 static 메서드를 인터페이스에서 helper 메서드로 사용하는 예시를 들고 있습니다. 여기서 말하는 helper 메서드는 주목적으로서 사용되는 것이 아닌, 다른 객체를 돕기 위해서 특정 목적으로 만들어진 메서드입니다.

     

    이러한 이유는 클래스의 static 메서드처럼 사용하기보다는, 인터페이스 내부적으로 필요한 것을 정의해두고 사용하는데 목적이 있다고 추측됩니다. 

     

    마찬가지로 구현이 강제되지 않습니다.

    // Test 인터페이스
    public interface Test {
    
        static String staticMethod() {
            return "hello ";
        }
    
        default String defaultMethod(String param) {
            return staticMethod() + param;
        }
    }
    
    
    // Test 인터페이스 구현한 TestImpl
    public class TestImpl implements Test {
    
        @Override
        public String defaultMethod(String param) {
            return Test.super.defaultMethod(param);
        }
    
        public static void main(String[] args) {
            TestImpl testImpl = new TestImpl();
    
            System.out.println(testImpl.defaultMethod("world"));
        }
    }

    Test 인터페이스에서 static 메서드는 default와 마찬가지고 구현 클래스에서 강제로 구현할 필요 없습니다. 

     

    staticMethod()defaualtMethod()의 철저한 helper class로 사용하고 있습니다. 이를 외부에서 사용하여 주목적으로 사용하는 것이 아닌, defaultMethod() 하나를 돕는 목적입니다. 


    5) 정리

    default, static 메서드 모두 인터페이스에서 구현되지만, 이를 구현한 클래스에서는 강제성이 없습니다. 그렇기 때문에 더 유연한 개발을 가능하게 하고, 하위 호환성의 문제도 피할 수 있다고 생각합니다. 

     

    주니어 개발자로서의 부족한 생각으로는, 이렇게까지 유연하게 된다면 기존의 인터페이스가 가진 규칙과 너무 멀어지는 게 아닌가 생각이 듭니다. 클래스와의 경계도 조금 모호해지는 느낌이고요.

     

    그럼에도 불구하고 사용하는 입장에서는 괜찮은 변화입니다. 해당 인터페이스를 구현한 객체와 관계없이, 필요한 기능을 언제든지 정의할 수 있으니까요. 구현한 객체에서는 필요할 때 이를 사용하면 되고요. 

     

     

    반응형

    댓글

Designed by Tistory.