JAVA 인터페이스

금송·2024년 9월 10일
0

이론

목록 보기
16/17
post-thumbnail

미리보기

추상클래스와 잘 구분해서 외우기.

인터페이스 “구현” → 타입으로 선언 가능 (참조 타입으로 사용할 수 있음)

public interface Sample {
	// 추상 메서드
	// 상수
}
// public class Sample

// 인터페이스 부모 클래스 선언 폼
[접근제어자] interface 인터페이스명 {
	[접근제어자] 타입 메소드명(); // 추상 메서드
}

// 자식 클래스 메서드 선언
[접근제어자] class 클래스명 implements 인터페이스명 {
    @Override
    [접근제어자] 타입 메서드명() {

    }	
}

인터페이스

여러 클래스에서 반드시 구현해야 할 공통 기능들을 틀만 잡아둔 ‘타입’

→ 객체의 기능을 정의한 ‘껍데기’

인터페이스를 구현하는 폼이 무조건 있어야 한다. 즉 어떠한 객체가 있고 그 객체가 특정한 인터페이스를 사용한다면 그 객체는 반드시 인터페이스의 메소드를 구현해야 함.

다형성 - 형태가 다양함

추상클래스 vs 인터페이스

  • 추상클래스
    • 다중 상속 불가
    • 필드, 생성자, 메소드 가능
  • 인터페이스
    • 다중 구현/상속 가능
    • 상수, 추상 메소드, default 메소드만 가능

인터페이스 사용 예시

package chap08.payment;

public interface Payment {
    void processPayment(double amount); //결제를 처리하는 기능 담당
}
package chap08.payment;

// CreditCard 클래스에서 결제 로직 구현
public class CreditCard implements Payment {
    private String cardNumber;     // 신용카드 결제에 필요한 카드 번호

		// 생성자 초기화
    CreditCard(String cardNumber){
        this.cardNumber = cardNumber;
    }
    @Override
    public void processPayment(double amount) {
        // amount 결제 로직
        System.out.println("Processing credit card payment of $" + amount + " using card number: " + cardNumber);
    }
}
package chap08.payment;

public class Paypal implements Payment {
    private String email;

    public Paypal(String email){
        this.email = email;
    }

    @Override
    public void processPayment(double amount){
        System.out.println("Processing credit card payment of $" + amount + " using card number: " + email);
        // togo 실제 Paypal 결제 로직
    }
}
package chap08.payment;

import java.util.ArrayList;

public class PaymentProcess {
    public static void main(String[] args){
        // 객체 생성 (paypal, creditcard)

        CreditCard creditCard = new CreditCard("5678-5345");
        creditCard.processPayment(670000);
        Paypal paypal = new Paypal("email@aws.com");
        paypal.processPayment(670000);

        // 인터페이스를 타입으로 선언 가능
        Payment creditCar = new CreditCard("5678-5345");
        creditCar.processPayment(670000);
        Payment paypa = new Paypal("email@aws.com");
        paypa.processPayment(670000);

    }
}
//출력
Processing credit card payment of $670000.0 using card number: 5678-5345
Processing credit card payment of $670000.0 using card number: email@aws.com
Processing credit card payment of $670000.0 using card number: 5678-5345
Processing credit card payment of $670000.0 using card number: email@aws.com

인터페이스 필요 이유

새로운 코드를 추가하더라도 코드의 구조를 크게 바꾸지 않고 추가 가능. → 유연한 확장이 가능

void : 리턴타입 
processPayment : 메소드명 - 어떤 역할인지 명시
(double amount) : 매개변수 - 결제 처리에 필요한 값 명시

인터페이스 구현

요구사항

다음은 어떤 동물원의 사육사가 하는 일이다.

난 동물원(zoo)의 사육사(zookeeper)이다.
육식동물(predator)이 들어오면 난 먹이를 던져준다(feed).
 - 호랑이(tiger)가 오면 고기(meat)를 던져준다.
 - 사자(lion)가 오면 생선(fish)를 던져준다.
  • 인터페이스를 사용하지 않은 코드
    package chap08.zoo;
    
    public class Zookeeper {
        void feed(Lion lion){
            System.out.println("feed fish");
        }
    
        void feed(Tiger tiger){
            System.out.println("feed meat");
        }
    }
    package chap08.zoo;
    
    public class Tiger {}
    package chap08.zoo;
    
    public class Lion {}
    package chap08.zoo;
    
    /*
    * 인터페이스를 사용하지 않고
    * 사육사가 각 동물마다 다른 먹이를 주는
    * zookeeper.feed() 메서드 호출
    * */
    public class Zoo {
        public static void main(String[] args){
            Zookeeper zookeeper = new Zookeeper();
    
            Lion lion = new Lion();
            zookeeper.feed(lion);
    
            Tiger tiger = new Tiger();
            zookeeper.feed(tiger);
        }
    }
    
    //결과 값
    feed fish
    feed meat
  • 인터페이스를 사용한 코드
    package chap08.zoo2;
    
    public interface Predator {
        String getFood();
    }
    package chap08.zoo2;
    
    public class Zookeeper {
        void feed(Predator predator){
    //        System.out.println("feed meat");
            System.out.println("feed " + predator.getFood()); // getFood()는 다형성을 가지고 있음. Tiger, Lion
        }
    }
    
    package chap08.zoo2;
    
    public class Tiger implements Predator{
        @Override
        public String getFood() {
            return "meat";
        }
    }
    package chap08.zoo2;
    
    public class Lion implements Predator {
        @Override
        public String getFood() {
            return "fish";
        }
    }
    package chap08.zoo2;
    
    /*
    * 인터페이스를 사용
    * 사육사가 각 동물마다 다른 먹이를 주는
    * zookeeper.feed() 메서드 호출
    * */
    public class Zoo {
        public static void main(String[] args){
            Zookeeper zookeeper = new Zookeeper();
    
            Lion lion = new Lion();
            zookeeper.feed(lion);
    
            Tiger tiger = new Tiger();
            zookeeper.feed(tiger);
    
            // Predator lion2 = new Lion();
            // Predator tiger2 = new Tiger();
        }
    }

인터페이스 타입 선언

인터페이스 변수;
변수 = 구현 객체;
인터페이스 변수 = 구현객체;

예시 코드

Predator anything = new Lion(); or Predator anything = new Tiger();

개발 코드에서 인터페이스는 클래스의 필드, 생성자 또는 메소드의 매개 변수, 생성자 또는 메소드의 로컬 변수로 선언될 수 있다.

인터페이스 상속

인터페이스도 상속이 가능.

클래스와 차이점은 다중 상속이 가능하다.

public interface 하위인터페이스 **extends 상위인터페이스1, 상위인터페이스2, ...** { // 다중 상속 가능
		...
}

하위 인터페이스를 구현하는 클래스는 하위 메소드 뿐만 아니라 상위 인터페이스의 모든 추상 메소드에 대한 실체 메소드를 갖고 있어야 함.

  • 상속 실습 코드
    package chap08.inherit;
    
    public interface InterfaceA {
        void methodA();
    }
    package chap08.inherit;
    
    public interface InterfaceB {
        void methodB();
    }
    package chap08.inherit;
    
    public interface InterfaceC extends InterfaceA, InterfaceB {
        void methodC();
    }
    package chap08.inherit;
    
    // InterfaceC 구현체 클래스
    public class ImplementsC implements InterfaceC {
        @Override
        public void methodC() {
            System.out.println("ImplementsC.methodC");
        }
    
        @Override
        public void methodA() {
            System.out.println("ImplementsC.methodA");
        }
    
        @Override
        public void methodB() {
            System.out.println("ImplementsC.methodB");
        }
    }
    
    package chap08.inherit;
    
    public class InterfaceInheritExample {
        public static void main(String[] args){
            ImplementsC impleC = new ImplementsC();
            impleC.methodA();
            impleC.methodB();
            impleC.methodC();
            System.out.println("-----------------------------");
            // impleC에서 가진 메서드와 InterfaceA에 가진 메서드에 공통적으로 가진 메서드만 사용 가능
            InterfaceA interfaceA = impleC;
            interfaceA.methodA();
            System.out.println("-----------------------------");
            InterfaceB interfaceB = impleC;
            interfaceB.methodB();
            System.out.println("-----------------------------");
            InterfaceC interfaceC = impleC;
            interfaceC.methodA();
            interfaceC.methodB();
            interfaceC.methodC();
    
        }
    }
    
    //결과
    ImplementsC.methodA
    ImplementsC.methodB
    ImplementsC.methodC
    -----------------------------
    ImplementsC.methodA
    -----------------------------
    ImplementsC.methodB
    -----------------------------
    ImplementsC.methodA
    ImplementsC.methodB
    ImplementsC.methodC

컨트롤 + 알트 + B // 해당 메서드 사용 위치 확인

인터페이스 다형성

다형성이란 하나의 타입에 대입되는 객체에 따라 실행 결과가 다양한 형태로 나오는 성질

  • 다형성 실습 코드 프로그램 소스코드는 크게 변함이 없고 구현 객체를 교체하면서 프로그램의 실행 결과가 다양해지는 것 → 인터페이스의 다형성
    package chap08.polymorphism;
    
    public interface ProfileRepository {
        void save();
    }
    package chap08.polymorphism;
    
    public class ProfileService {
        ProfileRepository repository;// 인터페이스는 매개변수에서도, 필드에서도 선언 가능.
    
        void saveProfile(){
            // 만약 다른 저장소가 추가된다면 객체 생성부만 바꿔 끼워주면 됨
            repository = new ProfileDBRepository();
            repository.save();
        }
    		// == ProfileRepository의 구현체를 매개값으로 전달하여 repository에 변화가 있어도 사용 가능한 코드로 수정
         void saveProfile(ProfileRepository repository){
            repository.save();
         }
    }
    
    package chap08.polymorphism;
    
    // 구현 클래스
    public class ProfileDBRepository implements ProfileRepository {
        @Override
        public void save() {
            System.out.println("DB에 프로필 저장하는 기능");
        }
    }
    package chap08.polymorphism;
    
    // 구현 클래스
    public class ProfileMemoryRepository implements ProfileRepository{
    
        @Override
        public void save() {
            System.out.println("메모리에 프로필 저장하는 기능");
        }
    }

자동타입변환

구현 객체가 인터페이스 타입으로 변환되는 것은 자동타입변환에 해당.

자동 타입변환은 프로그램 실행 도중에 자동으로 타입 변환이 일어나는 것을 말함.

인터페이스명 변수 = 구현 객체; // 구현객체로 자동 타입 변환.
  • 자동 타입 변환 실습 코드
    public interface Vehicle {
        void run();
    }
    public class Bus implements Vehicle {
        // 추상 클래스와 비슷하게 메서드를 오버라이딩 해주어야 컴파일 오류가 나지 않는다.
        // -> 구현 클래스에는 껍데기만 선언 되어 있기 떄문에
        // 추상 클래스와의 차이 : 인터페이스는 다중 구현이 가능함.
        @Override
        public void run() {
            System.out.println("버스가 달려갑니다 :)");
        }
    }
    public class Taxi implements Vehicle {
        @Override
        public void run() {
           System.out.println("택시가 달려갑니다 :-)");
        }
    }
    public class Driver {
        public void drive(**Vehicle vehicle**) {  // 구현 객체 vehicle 
            vehicle.run();    // 구현 객체의 run() 메소드가 실행됨
        }
    }
    public class DriverExample {
        public static void main(String[] args) {
            Driver driver = new Driver();
    
            Vehicle bus = new Bus();
            driver.drive(bus); // 전달한 구현 객체로 자동 타입 변환 : Vehicle vehicle = bus;
    
            Vehicle taxi = new Taxi();
            driver.drive(taxi); // 전달한 구현 객체로 자동 타입 변환 : Vehicle vehicle = taxi;
        }
    }
    // 결과
    택시가 달려갑니다 :-)
    버스가 달려갑니다 :)

객체타입확인 (instanceof)

어떤 구현 객체가 인터페이스 타입으로 변환 되었는지 확인할 수 있는 방법 → instanceof 연산자

if(xxx instanceof yyy){
	...
}
// 결과는 true, false로 리턴
package chap08;

public class Driver {
    void drive(Vehicle vehicle){
        // instanceof 연산자
        if(vehicle instanceof Bus){
            System.out.println("Bus//");
        } else if(vehicle instanceof Taxi) {
            System.out.println("Taxi//");
        }
        //if 조건문 대신 switch case 문 (JDK 17부터 사용 가능)
        // Pattern matching for Switch
        switch (vehicle){
            case Bus bus -> System.out.println("Bus//");
            case Taxi taxi -> System.out.println("Taxi//");
            default -> System.out.println("");
        }
        vehicle.run();
    }
}

//결과
Bus//           //true여서 결과값 노출
Bus//
버스가 달려갑니다 :)
Taxi//
Taxi//
택시가 달려갑니다 :-)

디폴트 메서드

자바8 버전 이후부터는 디폴트 메소드(default method)를 사용할 수 있다.

인터페이스의 메소드는 구현체를 가질 수 없지만 디폴트 메소드를 사용하면 실제 구현된 형태의 메소드를 가질 수 있다. 예를 들어 Predator 인터페이스에 다음과 같은 디폴트 메소드를 추가할 수 있다.

package chap08.defaultmethod;

public class Lion implements Predator {
    @Override
    public String getFood() {
        return "fish";
    }
}
package chap08.defaultmethod;

public class Tiger implements Predator {
    @Override
    public String getFood() {
        return "meat";
    }
}
interface Predator {
	String getFood();

	// 디폴트 메소드
	default void printFood() {   
		System.out.printf("my food is %s\n", getFood());
	}
}

//Zoo class에서 나온 결과
my food is fish
my food is meat

디폴트 메소드는 메소드명 가장 앞에 default라는 키워드를 붙이면 된다.

이렇게 Predator 인터페이스에 디폴트 메소드를 구현하면 Predator 인터페이스를 구현한 Tiger, Lion 등의 실제 클래스는 printFood() 메소드를 구현하지 않아도 공통으로 사용할 수 있다.
그리고 디폴트 메소드는 오버라이딩이 가능해서 printFood 메소드를 실제 클래스에서 다르게 구현하여 사용할 수도 있다.

profile
goldsong

0개의 댓글