Java

[Java]기본 메서드 또는 디폴트 메서드(Default Method)

DevStory 2022. 5. 12.

기본 메서드(Default Method)

Java 8에 도입된 기본 메서드는 default 키워드를 사용하여 인터페이스(interface)에서 메서드를 구현하는 것입니다.

 

기본 메서드 도입 이전에는 인터페이스에 추상 메서드만 작성할 수 있었습니다. 추상 메서드는 메서드 본문이 없는 메서드입니다.

 

다음 예제는 인터페이스 내부에 메서드 본문이 없는 추상 메서드가 존재하므로 정상적으로 컴파일됩니다.

public interface InterfaceTest {
  void printValue(String val);
}

하지만, 다음 예제는 추상 메서드에 메서드 본문이 존재하므로 컴파일 에러가 발생합니다.

public interface InterfaceTest {
  void printValue(String val) {
    System.out.println("val");
  };
}

실행 결과

Java 8 이전에는 인터페이스 내부의 추상 메서드를 재정의하기 위해 인터페이스를 상속받는 클래스에서 추상 메서드를 재정의(Overriding)해야만 했습니다. 하지만, Java 8부터 도입된 default 키워드를 사용하면 인터페이스에서 메서드 본문을 구현할 수 있습니다.

 

다음 예제는 default 키워드를 사용하여 인터페이스에서 기본 메서드를 정의하였으며, 객체를 생성하기 위해 인터페이스를 상속받는 클래스를 선언합니다.

public interface InterfaceTest {
  default void printValue(String val) {
    System.out.println(val);
  }
}

public class ClassTest implements InterfaceTest{
}

public class Main {
  public static void main(String args[]) {
    ClassTest obj = new ClassTest();
    obj.printValue("Hi!!!");
  }
}

실행 결과

Hi!!!

그리고 인터페이스에 정의된 기본 메서드는 구현 클래스에서 재정의할 수 있습니다. 다음 예제는 재정의된 메서드가 호출됩니다.

public interface InterfaceTest {
  default void printValue(String val) {
    System.out.println(val);
  }
}

public class ClassTest implements InterfaceTest{
  public void printValue(String val) {
    System.out.println("Override");
    System.out.println(val);
  }
}

public class Main {
  public static void main(String args[]) {
    ClassTest obj = new ClassTest();
    obj.printValue("Hi!!!");
  }
}

실행 결과

Override
Hi!!!

기본 메서드 필요성 1. 단순화

여러 클래스에서 한 개의 인터페이스를 구현해야 하는 경우 모든 클래스에서 메서드를 구현해야 합니다. 공통적으로 사용되는 메서드는 인터페이스에서 기본 메서드로 정의하면 더 이상 클래스에서 추상 메서드를 구현하지 않아도 됩니다. 불필요한 작업은 줄이고 개발 속도는 높아지므로 작업이 단순화됩니다.

반응형

기본 메서드 필요성 2. 람다식

Java 8 이전에는 컬렉션 객체를 순회해야 하는 경우 반복자(Iterator) 객체를 생성하고 hasNext() 메서드를 사용했습니다. 다음 예제는 컬렉션 객체를 순회하는 가장 일반적인 방법입니다.

ArrayList aList = new ArrayList<String>();
aList.add("One");
aList.add("Two");
aList.add("Three");

// iterator() 메서드를 호출하여 Iterator 객체를 가져옵니다. 
Iterator iterator = aList.iterator();
 
// hasNext() 메서드를 사용하여 컬렉션 객체를 순회합니다.
while( iterator.hasNext() ){
  // next() 메서드를 사용하여 컬렉션 객체의 현재 요소를 가져옵니다.
  System.out.println( iterator.next() );
}

이제 위 예제처럼 반복자 객체를 생성하지 않아도 됩니다. Java 8부터 Iterable 인터페이스에 forEach() 기본 메서드가 추가되었기 때문입니다.

 

Iterable 인터페이스의 forEach() 메서드를 사용하여 위 소스코드를 간결하게 만들 수 있습니다.

public class Main {
  public static void main(String args[]) {
    ArrayList aList = new ArrayList<String>();
    aList.add("One");
    aList.add("Two");
    aList.add("Three");

    aList.forEach((item) -> System.out.println(item));
  }
}

실행 결과

One
Two
Three

다중 상속

클래스에서 여러 개의 인터페이스를 구현할 수 있으므로 다중 상속에 대해 의문점이 발생할 수 있습니다. 각각의 인터페이스에는 동일한 이름, 매개변수, 반환 타입을 가지는 기본 메서드가 존재하는 경우 클래스에서 컴파일 에러가 발생합니다.

public interface interface001 {
  default void defaultMethod() {
    System.out.println("interface001의 defaultMethod() 메서드");
  }
}

public interface interface002 {
  default void defaultMethod() {
    System.out.println("interface002의 defaultMethod() 메서드");
  }
}

public class TestClass implements
        interface001, interface002 {
}

실행 결과

죽음의 다이아몬드라고 불리는 다중 인터페이스 상속으로 인해 컴파일 에러가 발생합니다.

 

TestClass 클래스는 어떤 인터페이스의 defaultMethod() 메서드를 호출해야 하는지 모르므로 충돌이 발생합니다.

 

위 문제를 해결하려면, 클래스에서 충돌이 발생한 메서드를 구현합니다.

public interface interface001 {
  default void defaultMethod() {
    System.out.println("interface001의 defaultMethod() 메서드");
  }
}

public interface interface002 {
  default void defaultMethod() {
    System.out.println("interface002의 defaultMethod() 메서드");
  }
}

public class TestClass implements
        interface001, interface002 {
  public void defaultMethod() {
    System.out.println("TestClass 클래스의 defaultMethod() 메서드");
  }
}

또 다른 방법으로 super 키워드와 Override 어노테이션을 사용하여 기본 메서드를 재정의할 수 있습니다.

public interface interface001 {
  default void defaultMethod() {
    System.out.println("interface001의 defaultMethod() 메서드");
  }
}

public interface interface002 {
  default void defaultMethod() {
    System.out.println("interface002의 defaultMethod() 메서드");
  }
}

public class TestClass implements
        interface001, interface002 {
  @Override
  public void defaultMethod() {
    interface001.super.defaultMethod();
    interface002.super.defaultMethod();
  }
}

정리

  • 기본 메서드는 default 키워드를 사용하여 인터페이스에서 메서드를 구현할 수 있습니다.
  • 공통적으로 사용되는 메서드는 인터페이스에서 기본 메서드로 구현할 수 있습니다.
반응형

댓글