Java Default Method

Martin the dog·2023년 11월 2일

Default 왜 필요할 까?

Java에서도 다른 객체지향형 언어들 처럼 상속과 구현의 개념이 있다. 이때 Java에서는 Interface를 통해 구현해야할 메써드들을 강제함으로 코드에 통일성을 부여해왔다. 하지만 Interface의 특성상 메써드를 추가하는데 큰 제약이 있다.

public interface Animal{
	void walk();
    int getAge();
    int getWeight();
}

public class human implements Animal{~}
public class monkey implements Animal{~}
... x 100

이렇게 설계를 Animal Interface를 이용하여 다양한 클래스들을 구현한 상태에서 만약 동물의 키를 구하는 getHeight 메써드를 추가했다고 생각하자.
그러면 개발자들은 저 Animal을 구현하는 모든 클래스들을 찾아서 getHeight메써드들을 전부 구현해야한다.

이를 막고자 default 메써드가 등장하였다.

default Method

만약 Interface Animal에 getHeight를 default로 정의한다면 상속받는 클래스에서 구현해야할 의무가 없어진다!

default Method를 이용한 interface는 abstract class와 큰 차이가 없는 것 같은데 과연 그럴까? 답은 아니다! abstract class의 경우 기본형 타입을 정의할 수 있고 상속때 하나의 abstract class만 상속받을 수 있다. 하지만 default를 사용하는 interface의 경우 기본형 타입을 정의할 수 없고 상속시 여러개의 Interface를 상속 받을 수 있다.

그렇다면 이러한 default method는 어떻게 사용해야 할까?

1. 선택형 메써드

다음과 같은 interface가 있다고 하자.

public interface animal{
	public void fly();
    public void walk();
}

이 animal interface를 dog와 bird class가 구현한다고 생각해보자.
bird는 날아가는 기능이 있기 때문에 둘다 구현을 해줄 필요가 있다.

public class bird implements animal{
	public void fly(){
    	System.out.println("fly away");
    }
    public void walk(){
    	System.out.println("walking around");
     }
}

하지만 dog class의 경우 걷기만 필요하지만 interface에 정의된 모든 함수들을 구현해햐 하기 때문에 아무 기능도 하지 않는 빈 껍데기를 구현해 줘야 한다.

public class dog implements animal{
	public void fly(){
    	throw new UnsupportedOperationException();
    }
    public void walk(){
    	System.out.println("walking around");
     }
}

하지만 dog class 처럼 fly()가 필요없는 객체들이 수백개 있다면?
수백개 다 필요없는 기능을 구현해야 하는 낭비가 크다. 만약 default 메써드를 정의한다면 interface 하나에서 빈껍데기 구현이 완료된다.

2. Semi-다중상속

Java에서는 다중상속이 안된다.하지만 여러개의 interface를 implements할 수는 있다. 이때 interface에 default 함수를 통해 메써드들을 미리 구현해 둔다면? 다중상속과 거의 유사한 코드 재사용을 이룰 수 있다.

먼저 이를 이루기 위해서는
1. 각 인터페이스에서 기능이 중복되지 않는 default 메써드들을 정의한다.
2. 인터패아수를 상속 받는다.
3. default 메써드를 제외하고 나머지 메써드들을 구현한다.

끝!

default 메써드의 충돌

상속을 하다보면 매번 나오는 문제가 있다. 같은 함수가 상속 되면서 여러번 override되었을 때 어떤 메써드가 실행될까?
순위는 다음의 규칙에 따라 정해진다.
1. 인터페이스와 클래스가 구현하고 있을 시 클래스가 이긴다.
2. 인터페이스(클래스) B가 인터페이스(클래스)A를 구현(상속) 할때 같은 default method를 구현한다면 언제나 구현(상속)을 하고 있는 B가 이긴다.

다음은 예시 코드이다

public interface A{
	default void hello(){
    	System.out.println("this is hello from A");
    }
}
public interface B implements A{
	default void hello(){
    	System.out.println("this is hello from B");
    }
}
public class C implements A,B{
	public static void main(String args[]){
    	new C().hello();<-- 결과는?
    	return ;
    }
}

저 위의 순서를 정하는 규칙에 따라 마지막으로 hello를 구현한 B가 이기기 때문에 this is hello from B가 실행된다.

하지만 B가 A를 상속받고 있지 않는다면?

public interface A{
	default void hello(){
    	System.out.println("this is hello from A");
    }
}
public interface B{
	default void hello(){
    	System.out.println("this is hello from B");
    }
}
public class C implements A,B{
	public static void main(String args[]){
    	new C().hello();<-- 결과는?
    	return ;
    }
}

결과는 오류이다. 이럴때는 명시적으로 해결해 줘야한다.

public interface A{
	default void hello(){
    	System.out.println("this is hello from A");
    }
}
public interface B {
	default void hello(){
    	System.out.println("this is hello from B");
    }
}
public class C implements A,B{
	public void hello(){
    	B.super.hello();<-- 명시적으로 불러올 메써드를 지정함
    }
	public static void main(String args[]){
    	new C().hello();
    	return ;
    }
}

상위클래스를 부를 땐
클래스명.super.호출할 대상
을 해누다.

profile
Happy Developer

0개의 댓글