[JAVA] 14-1. 자바에서의 인터페이스 정의 및 역할

Re_Go·2024년 5월 27일
0

JAVA

목록 보기
17/37
post-thumbnail

자바에서 클래스의 상속은 주로 단일 상속을 지원합니다. 가령 A라는 상위 클래스와 B라는 상위 클래스가 있다면 C라는 하위 클래스는 A와 B 둘 중 하나만을 상속할 수 있는거죠.

그렇다고 다중 상속을 구현할 수 없는 것은 아닙니다. 인터페이스 객체를 여럿으로 상속 받는 방법으로 다중 상속 구현이 가능한데요. 이번 챕터에서는 자바에서 이러한 다중 상속을 가능케 하는 인터페이스에 대해서 알아보겠습니다.

1. 인터페이스?

인터페이스의 사전적 정의는 서로 다른 두 개의 시스템, 장치 사이에서 정보나 신호를 주고받는 경우의 접점이나 경계면을 의미하며, 간단히 얘기해서 사용자와 기기 사이에서 응답을 서포트 해주는 장치라고 보시면 되는데요.

자바에서의 인터페이스란 각 객체에 공통적으로 제공할 구현하지 않은 메서드들의 집합을 의미하는데요. 앞서 말씀드린 대로 클래스는 이러한 인터페이스를 여러 개 다중 상속 받아 각 인터페이스 마다의 메서드를 재정의 및 사용하는 것이 가능합니다.

또한 인터페이스는 이러한 정의만 한 메서드들을 갖고 있기 때문에, 추상 메서드의 연장 선상이라고도 할 수 있습니다.

이러한 인터페이스의 선언은 클래스를 선언하는 것과 같이 interface 키워드를 인터페이스 이름명 앞에 작성해주면 되며, 상속을 받을 경우 클래스 뒷쪽에 implements를 붙인 후 상속 받을 인터페이스를 차례대로 작성해주면 됩니다.

interface Shape{
}

class Rect implements Shape{
}

주의할 점은 인터페이스는 기본적으로 abstract 기능이 적용되어 있기 때문에 굳이 abstract 이름에 키워드를 명시하지 않아도 되며, 메서드의 네이밍에 접근 제어자는defaultpublic, ' private 만 사용이 가능합니다.

interface Shape{
	void calc1() {} // 유효 : 기본적인 클래스는 메서드에 접근 제어자를 명시하지 않으면  default지만, 인터페이스에서는 메서드에 public이 기본값으로 붙음
    private calc2() {} // 유효 : 자바 9 이상에서 유효
    default calc3() {} // 유효 : 기본 메서드
    protected calc4() {} // 무효 : 패키지 내의 모든 클래스에서 접근 가능하기 때문에 상속 받은 클래스만 재정의 해야하는 목적상 맞지 않습니다.
}

class Rect implements Shape{
}

또한 인터페이스에서 선언하는 필드는 기본적으로 public static final의 특성을 갖고 있는데요. 조금만 생각을 해봐도, 인터페이스의 상속을 받은 클래스에서 직접 구현을 해주어야 하는 만큼 인터페이스는 갖고만 있는 역할을 수행하기 때문에 어떠한 특정 필드의 값을 변경할 필요가 없고, 단지 제공하는 역할을 해야하기 때문입니다.

// 인터페이스의 기본 골자
interface Shape{
	// 필드
	public static double PI = Math.PI;
    // 메서드
	double calcArea();
	void output();
}

2. 인터페이스의 상속

우선 인터페이스를 상속하기 위해서는 클래스를 상속 받을 때와 마찬가지로 implements 키워드를 사용해 상속을 정의한 후 필드와 생성자를 작성해 줍니다.

class Rect implements Shape{

	private int width, height;

	public Rect() {}
}

그 다음 상속 받은 메서드를 구현해 주는데, 이때 구현해 줄 메서드를 구현하고 싶지 않은 경우 선언은 하되 반환 타입이 있을 경우 그 타입을, 없을 경우 아무것도 적지 않습니다.


	// 반환타입이 double 이므로 0.0 작성
	@Override
	public double calcArea() {
		return 0.0;
	}

	// 반환 타입이 void 이므로 작성 안해도 무관
	@Override
	public void output() {

	}

만약 구현을 한다면 추상 메서드를 상속 받아 작성 하는 것과 같이 재정의 해주면 되는데요. 이때 오버라이드 기호는 생략해도 됩니다. 어차피 컴파일러가 해당 메서드는 인터페이스로부터 상속 받은 메서드임을 알기 때문이죠.


	// 오버라이드 기호를 생략하고 재정의
	public double calcArea() {
		return (double) width * height;
	}

    // 오버라이드 기호를 붙이고 재정의
	@Override
	public void output() {
		System.out.println(
				"가로 : " + width + "\n" +
				"세로 : " + height + "\n" +
				"크기 : " + calcArea() + "\n"
		);
	}

그 다음 선언한 필드와 물려 받은 메서드의 조합을 이용해 코드를 실행해 주면 됩니다.

Rect rect = new Rect(5,8);	

rect.output(); // rect가 재정의 한 메서드가 호출

또한 클래스는 다른 클래스 상속 받고 있을 때 다른 인터페이스 또한 상속 받을 수 있는데요. 이 경우 클래스는 하나만, 인터페이스는 다중 상속을 받을 수 있습니다.

  1. 클래스 및 인터페이스 정의
// Eatable 클래스 정의
public class Eatable {
    // Eatable 클래스의 멤버 변수 및 메서드
    public void consume() {
        System.out.println("This is eatable.");
    }
}

// Cookable 인터페이스 정의
interface Cookable {
    void cook();
}

// Sellable 인터페이스 정의
interface Sellable {
    void sell();
}
  1. 상속 및 실행
public class Apple extends Eatable implements Cookable, Sellable {
    // Cookable 인터페이스 메서드 구현
    @Override
    public void cook() {
        System.out.println("Cooking an apple.");
    }

    // Sellable 인터페이스 메서드 구현
    @Override
    public void sell() {
        System.out.println("Selling an apple.");
    }

    // Apple 클래스의 추가 멤버 변수 및 메서드
    public void grow() {
        System.out.println("Growing an apple.");
    }

    public static void main(String[] args) {
        Apple apple = new Apple();
        apple.consume();  // Eatable 클래스의 메서드
        apple.cook();     // Cookable 인터페이스의 메서드
        apple.sell();     // Sellable 인터페이스의 메서드
        apple.grow();     // Apple 클래스의 메서드
    }
}
profile
인생은 본인의 삶을 곱씹어보는 R과 타인의 삶을 배워 나아가는 L의 연속이다.

0개의 댓글