[그린컴퓨터] Server/JAVA(객체 지향 프로그래밍)

인터페이스란 { 구현 코드가 없는 인터페이스, 클래스에서 인터페이스 구현하기, 인터페이스 구현과 형 변환 }

Ben의 프로그램 2023. 6. 2. 11:49
728x90

인터페이스?

  • 앞선 장에서는 구현하지 않은 메서드를 포함한 추상 클래스에 대해 배웠습니다.
  • 이제 모든 메서드가 추상 메서드로만 이루어진 인터페이스에 대해 알아보겠습니다. 
  • 구현 코드가 없는 인터페이스가 어떤 쓰임이 있는지, 클래스가 인터페이스를 구현하는 것은 어떤 의미인지 살펴봅시다. 
  • 인터페이스와 다형성의 관계를 이해함으로써 자바 프로그램을 만들 때 인터페이스 설계가 왜 중요한지도 알 수 있습니다. 

구현 코드가 없는 인터페이스

  • 인터페이스(Interface)는 클래스 혹은 프로그램이 제공하는 기능을 명시적으로 선언하는 역할을 합니다. 
  • 인터페이스는 추상 메서드와 상수로만 이루어져 있습니다. 
  • 구현된 코드가 없기 때문에 당연히 인터페이스로 인스턴스를 생성할 수도 없습니다. 
  • 그렇다면 구현 코드도 없는 인터페이스는 어떻게 사용하는 걸까요?
  • 인터페이스를 직접 만들어 보면서 살펴봅시다. 

인터페이스 만들기

  • 인터페이스를 사용해 간단한 계산기 프로그램을 만들어 보겠습니다. 
  • 이클립스에서 인터페이스를 만들 때는 New - Interface 를 클릭하여 생성합니다. 
public interface Calc {
	double PI = 3.14;  // 인터페이스에서 선언한 변수는 컴파일 과정에서 자동으로 상수로 변환됨
	int ERROR = -99999;
	
	int add(int num1, int num2);  // 인터페이스에서 선언한 메서드는 컴파일 과정에서 자동으로 추상 메서드로 변환됨
	int substract(int num1, int num2);
	int times(int num1, int num2);
	int divide(int num1, int num2);
}
  • 이 인터페이스는 계산기를 만들기 위해 선언한 코드입니다. 
  • Calc 인터페이스에는 원주율을 뜻하는 PI 변수와 오류가 났을 때 사용할 ERROR 변수, 그리고 사칙연산에 해당하는 메서드 4개를 선언했습니다. 
  • 인터페이스에 선언한 변수는  public static final 예약어를 쓰지 않아도 컴파일 과정에서 자동으로 상수로 변환됩니다. 
  • 인터페이스에 선언한 메서드는 public abstract 예약어를 쓰지 않아도 컴파일 과정에서 자동으로 추상 메서드로 변환됩니다. 

클래스에서 인터페이스 구현하기

  • 클래스가 인터페이스를 사용하는 것을 '클래스에서 인터페이스를 구현한다 implement' 라고 표현합니다. 
  • 인터페이스에 선언한 기능을 클래스가 구현한다는 의미로 implements 예약어를 사용합니다. 
  • Calc 인터페이스를 Calculator 클래스에서 구현하는 방법은 다음과 같습니다. 
public class Calculator implements Calc{  // 오류 발생

}
  • 그런데, 이렇게 코드를 작성하면 다음과 같은 오류가 발생합니다.
  • 1. 구현하지 않은 추상 메서드를 구현하세요.
  • 2. Calculator 클래스를 추상 클래스로 만드세요.
  • 이러한 오류가 발생하는 이유는 Calculator 클래스에서 Calc 인터페이스를 구현한다고 했으므로 Calculator 클래스는 추상 메서드 4개를 포함합니다. 
  • 따라서 인터페이스를 구현하면서 받은 추상 메서드를 구현하지 않으면 Calculator 클래스도 추상 메서드가 되어야 하기 때문에 에러 메시지가 나오게 됩니다. 

  • 클래스 다이어그램에서 인터페이스를 구현하는 것은 점선으로 표시합니다.
  • 우선 Calc 의 4가지 추상 메서드 중 2가지만 Calculator 에서 구현해보겠습니다.
public abstract class Calculator implements Calc{

	@Override
	public int add(int num1, int num2) {
		return num1 + num2;
	}

	@Override
	public int substract(int num1, int num2) {
		return num1 - num2;
	}  

}
  • 위의 Calculator 클래스는 Calc 인터페이스를 구현하는데, 4개의 추상 메서드 모두 구현하지 않고 2개만 구현했으므로 추상 클래스가 됩니다. 
  • 이제 모든 메서드를 구현한 CompleteCalc 계산기 클래스를 만들어 보겠습니다. 
  • CompleteCalc 클래스에서는 Calculator 클래스를 상속받아 CompleteCalc 클래스를 만듭니다. 

클래스 다이어그램

public class CompleteCalc extends Calculator{

	@Override
	public int times(int num1, int num2) {
		return num1 * num2;
	}

	@Override
	public int divide(int num1, int num2) {
		if (num2 != 0)
			return num1/num2;
		else
			return Calc.ERROR;
	}
	
	public void showInfo() {
		System.out.println("Calc 인터페이스를 구현했습니다.");
	}
}
  • 이제 테스트 프로그램을 만들어서 CompleteCalc 클래스를 실행해 보겠습니다. 
public class CalculatorTest {

	public static void main(String[] args) {
		int num1 = 10;
		int num2 = 5;
		
		CompleteCalc calc = new CompleteCalc();
		System.out.println(calc.add(num1, num2));
		System.out.println(calc.substract(num1, num2));
		System.out.println(calc.times(num1, num2));
		System.out.println(calc.divide(num1, num2));
		calc.showInfo();
		System.out.println(calc.ERROR);
	}

}
  • CompleteCalc 자료형 변수 calc 를 생성하고 해당 변수에 CompleteCalc 의 인스턴스를 담았습니다. 
  • 왜냐하면 CompleteCalc 클래스만 구체적인 클래스이고 나머지 클래스들은 인스턴스를 생성할 수 없는 추상 클래스이거나 인터페이스이기 때문입니다. 
  • 그리고 calc 변수로 재정의된 메서드들인 add, substract, times, divide 와 CompleteCalc 클래스에서 정의한 메서드인 showInfo 메서드를 성공적으로 사용하여 결과값을 출력하는 것을 볼 수 있습니다. 
  • 여기서 궁금한 것이 하나 있지 않으신가요? 
  • calc.ERROR 이 코드는 어떻게 작동하는 걸까요?
  • 다른 메서드들은 interface 를 구현하거나 상속을 받은 다음 메서드를 재정의 하는 과정을 통해 메서드를 사용할 수 있게 되었습니다.
  • (제가 이해한 바로는 다음과 같습니다만, 아주 잘못된 이해 방식일 수 있음을 미리 말씀드립니다. 추후에  수정해야겠다는 깨달음이 있다면 바로 수정하러 오겠습니다.)
  • 인터페이스를 구현하는 클래스는 인터페이스의 모든 것을 구현해야 합니다. 
  • 우리의 예제를 통해서 생각해 보자면, Calc 인터페이스를 implement 한 Calculator 클래스는 Calc 인터페이스에 있는 모든 멤버들을 implement 해야 합니다. 
  • 따라서 Calc 인터페이스에서 정의한 ERROR 와 같은 상수는 Calc 인터페이스를 implement 한 Calculator 클래스에 들어있고, Calculator 클래스를 상속 받은 CompleteCalc 클래스의 인스턴스를 통해서 ERROR 상수를 사용할 수 있습니다. 
  • * 수정 * 
  • 제가 이해했던 내용이 틀렸습니다. 이전과 마찬가지로 제가 이해한 내용이 틀리면 수정하러 다시 오겠습니다. 
  • 상수 데이터는 스택 메모리나 힙 메모리에 생성되는 것이 아니라 데이터 영역에 바로 생성됩니다. 
  • 따라서 Calc 인터페이스에 int ERROR 로 선언한 변수는 컴파일러가 자동으로 public static final 로 선언하기 때문에 상수로 정의됩니다.
  • 따라서 Calc 인터페이스 밖 클래스에서 Calc 인터페이스 명으로 해당 상수를 자연스럽게 사용할 수 있습니다. 

인터페이스 구현과 형 변환 

클래스 다이어그램

  • 우리의 예제를 다시 살펴보겠습니다.
  • Calculator 클래스는 인터페이스에서 선언한 추상 메서드 중 일부 메서드만 구현했으므로 당연히 추상 클래스입니다.
  • 그리고 Calculator 클래스를 상속 받은 CompleteCalc 클래스에서 Calculator 클래스에서 구현하지 않은 나머지 추상 메서드를 모두 구현하고 showInfo( ) 메서드를 추가로 구현했습니다. 
  • 이런 과정에서 하위 클래스의 형 변환은 어떻게 이루어지는지 살펴보겠습니다. 
  • 상속 관계에서 하위 클래스는 상위 클래스 자료형으로 묵시적 형 변환할 수 있다고 했습니다. 
  • 인터페이스도 마찬가지입니다. 
  • CompleteCalc 클래스는 상위 클래스인 Calculator 형이면서, Calc 인터페이스를 구현하였으므로 Calc 형이기도 합니다.
  • 그래서 다음과 같이 Calc 형으로 선언한 변수에 CompleteCalc 의 인스턴스를 대입할 수 있습니다. 
Calc calc = new CompleteCalc( );
  • 이 calc 변수가 사용할 수 있는 메서드를 살펴볼까요? 
  • Calc 에서 선언한 추상 메서드들(add, substract, times, devide)은 사용 가능합니다.
  • 그런데, CompleteCalc 클래스에서 추가로 구현한 showInfo( ) 메서드는 사용 불가능합니다. 
  • 즉 Calc 형으로 선언한 변수에서 사용할 수 있는 메서드는 Calc 인터페이스에 선언한 메서드뿐입니다. 

정리하자면

  • 인터페이스를 구현한 클래스는 해당 인터페이스형으로 묵시적 형 변환이 가능합니다.(상속과 같다)
  • 형 변환되었을 때 사용할 수 있는 메서드와 상수는 인터페이스에서 선언한 메서드와 상수입니다.