Skip to content

Day 16(21.09.28)

Byeong Gwan Seo edited this page Sep 28, 2021 · 1 revision

23. 인터페이스(Interface)

(1) 인터페이스의 역할

Java에서 인터페이스는 객체의 사용 방법을 정의한 타입이다.또한, 인터페이스는 개발 코드와 객체가 서로 통신하는 접점 역할을 하기에, 개발 코드가 인터페이스의 메소드를 호출하면 인터페이스가 객체의 메소드를 호출한다. 때문에 개발 코드는 객체의 내부 구조까지는 파악할 필요는 없고, 인터페이스가 제공하는 객체의 메소드만 알고 있으면 된다. 그런데 여기서 한 가지 드는 의문.

왜 객체의 메소드를 호출하지 않고 인터페이스를 두는 것인가?

이유는 개발 코드를 수정하지 않고 사용하는 객체를 변경하기 위함이다. 인터페이스는 다수의 객체들과 사용이 가능하기 때문에 객체에 따라 실행 내용과 리턴 값이 다를 수 있다. 때문에, 개발 코드 측면에서는 코드를 변경하지 않고도 실행 내용과 리턴 값을 다양화 할 수 있다는 장점이 있다.

(2) 인터페이스의 선언

인터페이스는 .java 형태의 src 파일로 작성되고, 컴파일러(javac.exe)를 통해 클래스 형태로 컴파일 되기 때문에 물리적인 형태는 클래스와 동일하다. 하지만 소스를 작성할때 선언하는 방법에서 차이를 보인다.

클래스의 선언
접근제어자 class 클래스명 {....}
인터페이스의 선언
접근제어자 interface 클래스명 {....}

또한, 클래스는 필드, 생성자, 메소드를 멤버로 가지는 것과 달리, 인터페이스는 상수와 메소드만 구성 멤버다. 또한 인터페이스는 추상 클래스와 마찬가지로 객체로 생성할 수 없으므로, 생성자를 가질 수 없다. 아래는 인터페이스 선언의 예시이다.

interface 인터페이스명 {
  //상수 필드
  타입 상수명 = ;
  
  //추상 메소드
  타입 메소드명(파라미터, ...);

  //디폴트 메소드
  default 타입 메소드명(파라미터, ...) {...}
 }

① 상수 필드 선언
인터페이스는 실체가 없는 객체 사용 설명서이므로 데이터를 저장할 필드를 선언할 수 없기 때문에, 객체 또는 스태틱한 필드를 선언할 수 없고, 상수만 선언할 수 있다. 인터페이스에 선언된 상수 필드는 모두 public static final 의 특성을 갖기 때문에 생략하더라도 컴파일 과정에서 자동으로 붙게 된다.

② 추상 메소드 선언
인터페이스를 통해 호출된 메소드는 최종적으로 객체에서 실행된다. 따라서 인터페이스의 메소드는 실행 블록{}이 필요 없는 추상 메소드로 선언한다. 인터페이스에 선언된 추상 메소드는 모두 public abstract의 특성을 갖기 때문에 public abstract를 생략하더라도 컴파일 과정에서 자동으로 붙게 된다.

③ 디폴트 메소드 선언
위에서 인터페이스의 메소드는 실행 블록이 필요 없는 추상 메소드로 선언한다고 했지만, 부득불 실행 블록이 필요한 메서드를 작성해야할 경우에 메소드의 리턴 타입 앞에 default를 붙여 디폴트 메소드로 사용할 수 있다. 이 경우엔 실행 블록을 사용할 수 있으며, public 특성을 갖기 때문에 public을 생략하더라도 컴파일 과정에서 자동으로 붙게 된다.

(3) 인터페이스 구현

코드가 인터페이스 메소드를 호출하면 인터페이스는 객체의 메소드를 호출한다. 그리고 객체는 인터페이스에서 정의된 추상 메소드와 동일한 이름, 파라미터 타입, 리턴 타입을 가진 실체 메소드를 가지고 있어야 한다. 이를 인터페이스의 구현(implement) 객체라고 하고, 구현 객체를 생성하는 클래스를 구현 클래스라고 한다.

① 구현 클래스

보통의 클래스와 동일하지만 인터페이스 타입으로 사용할 수 있음을 알려주기 위해 클래스 선언부에 implement 키워드를 사용하고, 인터페이스명을 적어주어야 한다.

public class 구현클래스명 implement 인터페이스명 {
//인터페이스에 선언된 추상 메소드의 실체 메소드 선언
}

구현 클래스에서 인터페이스의 추상 메소드들에 대한 실체 메소드를 작성할때는 인터페이스의 모든 메소드는 기본적으로 public 접근 제한을 갖는다는 점이다. 따라서, public 보다 더 낮은 접근 제한으로 메소드를 작성할 수 없다. 만약 public을 명시하지 않으면 컴파일 에러가 발생하게 된다.

이렇게 구현 클래스가 작성되면 new 연산자로 객체를 생성할 수 있다. 다만, 어떤 타입의 변수에 대입하느냐가 문제인데, 인터페이스로 구현 객체를 사용하려면 인터페이스 변수를 먼저 선언하고, 구현 객체를 대입해야한다. 참고로 인터페이스 변수는 참조 타입이므로 구현 객체가 대입되면 구현 객체의 주소를 저장한다.

//Coffee라는 인터페이스가 있고, 여러 종류의 커피를 구현 객체로 생성하려고 할때
Coffee coffee; //참조 변수 coffee

coffee = new Espresso(); // Espresso 구현 객체 생성
coffee.name(); //Espresso 구현 클래스의 name() 메소드 호출
coffee.price(); //Espresso 구현 클래스의 price() 메소드 호출

coffee = new CaffeLatte(); // CaffeLatte 구현 객체 생성
coffee.name();
coffee.price();

② 다중 인터페이스 구현 클래스

한 객체의 메소드를 복수의 인터페이스가 호출할 수 있으려면, 해당 객체는 복수의 인터페이스를 모두 구현해야 하는데, 이럴때 다중 인터페이스를 반영하도록 구현 클래스를 작성해야 한다.

public class 구현 클래스명 implements 인터페이스1, 인터페이스2 {
  //인터페이스1에 선언된 추상 메소드의 실체 메소드 선언
  //인터페이스2에 선언된 추상 메소드의 실체 메소드 선언
 }

위와 같이 다중 인터페이스를 구현할 경우엔 구현 클래스는 모든 인터페이스의 추상 메소드에 대한 실체 메소드를 작성해야 한다. 만약 하나라도 없다면 추상 클래스로 선언해야 한다.

//각 인터페이스와 인터페이스의 추상 메소드 선언
interface Car {
  void work();
}

interface Cannon {
  void fire();
}

interface MachineGun{
  void shoot();
}
//구현 클래스
class Tank implements Car, Cannon, MachineGun{
 //구현된 클래스에서 사용할 각 인터페이스의 실체 메소드 작성
 @Override
 public void work() {
  System.out.println("Tank is moving");
 }

 @Override
 public void fire() {
  System.out.println("Firing the cannon");
 }

 @Override
 public void shoot() {
  System.out.println("Firing COAX");
 }
}
public class TankExam {
 public static void main(String[] args) {
  //구현 클래스 Tank로 Tank 객체 생성
   Tank tank1 = new Tank();

   tank1.work();
   tank1.fire();
   tank1.shoot();
  }
}

Result

Tank is moving
Firing the cannon
Firing COAX

Clone this wiki locally