Adapter와 default 메소드
Adapter
- 인터페이스를 대신 상속
아래의 코드에서 Service 클래스에서는 MyInterface를 상속받았기 때문에 method2가 필요하지 않음에도 method2를 구현해야 한다. 이 때 사용하는 것이 adapter이다. adapter는 인터페이스를 대신 상속받아 빈 구현부를 만드는 클래스로, adapter클래스를 만든 후 이를 상속받아 원하는 메소드만 오버라이딩하여 구현하는 방식으로 사용한다.
public interface MyInterface {
public void method1();
public void method2();
}
class Service implements MyInterface{
@Override
public void method1() {
System.out.println("Service 클래스의 method1입니다")
}
//method2를 쓰지 않음에도 method2를 만들어야 함
@Override
public void method2() {
//nothing
}
}
//adapter 클래스
public class MyInterfaceAdapter implements MyInterface{
@Override
public void method1() {
}
@Override
public void method2() {
}
}
//adapter클래스를 상속받음
class MyService extends MyInterfaceAdapter{
@Override
public void method1(){
System.out.println("Hello! Myservice extends MyInterfaceAdapter");
}
}
하지만 이 경우, 다른 클래스를 상속받고자 할때 문제가 발생한다. 자바에서 클래스는 단 한개만 상속받을 수 있기 때문이다. 이럴 경우엔 어쩔 수 없이 인터페이스를 상속받아 모든 메소드를 구현하고, 사용하지 않는 메소드는 빈 메소드로 남겨놓게 된다. 사용하지 않는 메소드가 많아질 수록 코드가 지저분해진다.
class MyService2 extends Object implements MyInterface{
@Override
public void method1() {
System.out.println("MyService2의 method1입니다");
}
@Override
public void method2() {
//nothing
}
}
Default 메소드
1) Adapter역할의 수행
이에 따라, default 메소드가 등장하게 된다. default 메소드는 구현부까지 작성된 인터페이스의 기본 메소드로, 구현코드가 존재하기 때문에 추상메소드 abstract와 달리 반드시 구현을 요구하지 않는다. 따라서 사용하지 않는 메소드까지 상속받아 구현하는 불편함을 해결할 수 있으며, 원하는 메소드만 구현할 수 있게 된다.
public interface MyInterfaceDefault {
//추상(abstract) 메소드: 구현부 없음. 구현 클래스에서 반드시 구현코드를 작성
abstract void method1();
//default 메소드: 구현부{} 있음. 구현 클래스에서 반드시 작성할 필요가 없음
default void method2(){}
default void method3(){}
default void method4(){}
default void method5(){}
}
class MyService3 implements MyInterfaceDefault{
//추상 메소드 구현
@Override
public void method1(){
System.out.println("MyService3의 method1입니다");
}
//default: 원하는 메소드만 구현 가능
@Override
public void method3(){
System.out.println("MyService3의 method3입니다");
}
}
2) 중복된 코드 통합
default 메소드의 또 다른 사용으로는 중복된 기능을 통합하는 것이다. 아래의 코드에서 Duck의 swim과 Swan의 swim은 같은 기능을 수행한다.
interface Flyable{
void fly();
}
interface Swimmable{
void swim();
}
interface Walkable{
void walk();
}
class Duck implements Swimmable, Walkable{
@Override
public void swim() {
System.out.println("SWIM");
}
@Override
public void walk() {
System.out.println("WALK");
}
}
class Swan implements Flyable, Swimmable{
@Override
public void fly() {
System.out.println("SWIM");
}
@Override
public void swim() {
System.out.println("WALK");
}
}
이를 인터페이스의 default 메소드로 만들면 똑같은 기능을 하는 코드를 중복하여 작성할 필요가 없게 된다.
interface Flyable{
default void fly(){
System.out.println("FLY");
};
}
interface Swimmable{
default void swim(){
System.out.println("SWIM");
};
}
interface Walkable{
default void walk(){
System.out.println("WALK");
};
}
class Duck implements Swimmable, Walkable{
}
class Swan implements Flyable, Swimmable{
}
3) 기능 확장
Duck, Swan 클래스 모두 구현 코드를 작성하지 않아도 인터페이스의 default 메소드에 구현 코드가 있으므로 문제없이 동작하며, 인터페이스의 메소드를 호출하게 된다.
public class Main {
public static void main(String[] args) {
new Duck().swim(); // SWIM
new Duck().walk(); // WALK
new Swan().fly(); // FLY
}
}
또한, Swan 클래스에 Walkable 메소드를 추가하면 Walkable메소드의 default메소드인 walk()를 호출할 수 있게되고, 기능이 확장된다. 즉, default 메소드를 통해 인터페이스를 추가하는 것 만으로도 기능을 확장할 수 있게 되었다.
class Swan implements Flyable, Swimmable, Walkable{
}
public class Main {
public static void main(String[] args) {
new Duck().swim();
new Duck().walk();
new Swan().fly();
new Swan().walk();
}
}
3) static 메소드를 가질 수 있게 됨
- 구현 코드를 작성할 수 있게 되면서, static으로 선언하여 호출할 수 있게 되었다.(함수를 제공)
interface Ability{
static void sayHello(){
System.out.println("Hello");
}
}
public class Main {
public static void main(String[] args) {
Ability.sayHello(); // Hello
}
}
'Language > java' 카테고리의 다른 글
Java:Lambda 표현식 (0) | 2023.01.16 |
---|---|
Java: Functional Interface(함수형 인터페이스) (0) | 2023.01.16 |
인터페이스 심화 (0) | 2023.01.16 |
JAVA의 객체지향 프로그래밍(캡슐화/정보은닉/추상화/상속/다형성) (0) | 2023.01.10 |
java 기본 13: 중첩 클래스(중첩(인스턴스)클래스, 정적 중첩 클래스, 로컬 클래스, 익명 클래스 (0) | 2023.01.05 |