본문 바로가기

플밍 is 뭔들/JAVA

[자바] 인터페이스 (Interface)

※ 인터페이스란?
일종의 추상클래스로써 인터페이스는 추상클래스처럼 추상메서드를 갖지만 추상클래스보다 추상화 정도가 높아서 추상클래스와 달리 몸통을 갖춘 일반 메서드 또는 멤버변수를 구성원으로 가질 수 없다. 
인터페이스에는 오직 추상메서드상수만을 가진다.

추상클래스 : 부분적으로만 미완성이 되어있는 설계도
인터페이스 : 구현은 아무것도 되어있지 않고 밑그림만 그려져 있는 설계도


※ 인터페이스 작성

interface InterfaceTest {
      public static final int a =0;
      public void method1();
}

모든 멤버변수는 public static final 이어야 하며, 이를 생략할 수 있다.
모든 메서드는 public abstract 이어야 하며 이를 생략할 수 있다.

하지만 코드를 작성하다 보면 접근 제어자를 public이 아닌 다른 제어자로 정의하는것을 제외하고는 어떤 형태로 작성해도 오류메세지가 나지 않는다.
ex) 
final int a =0;
public int a =0;
static final int a =0;
public final int a =0;
....

하지만 이러한 경우는 컴파일 시에 컴파일러가 자동으로 public static final로 변경시켜준다고 한다.


※ 인터페이스간의 상속
인터페이스는 인터페이스로부터만 상속받을 수 있다.
인터페이스 간의 상속은 클래스간의 상속과는 달리 다중상속이 가능하다.

interface InterfaceTest {
      public final int a =0;
      public void method1();
}
public interface InterfaceTest2 {
      public void method2();
      public void method3();
}
public interface InterfaceTest3 extends InterfaceTest, InterfaceTest2{} //다중상속

이렇게 두개의 인터페이스를 상속받은 InterfaceTest3은 InterfaceTest1과 InterfaceTest2의 멤버를 모두 상속받는다.
그러면 이 InterfaceTest3을 클래스에 상속을 시킨다면 어떻게 될까?

public class InterfaceTestClass implements InterfaceTest3{
      @Override
      public void method1() { /* 구현 */}
      @Override
      public void method2() { /* 구현 */}
      @Override
      public void method3() { /* 구현 */}
}

바로 InterfaceTest1과 2에 있는 모든 멤버가 InterfaceTest3으로 상속되었기 때문에 InterfaceTest1과 2에 있는 모든 메서드를 구현해 줘야 한다.


※ 인터페이스를 이용한 다중상속
자바는 클래스의 다중상속을 지원하지 않는다. 하지만 인터페이스를 이용하면 다중상속이 가능하다.
다중상속의 방식은 아래와 같다.

public class Tv {

      protected boolean power;
      protected int channel;
      protected int volume;
      public void power(){power = !power;}
      public void channelUp(){channel++;}
      public void channelDown(){channel--;}
      public void volumeUp(){volume++;}
      public void volumeDown(){volume--;}
}

public class VCR {

      protected int counter;
       
      public void play(){/*Tape재생 구현*/}
      public void stop(){/*Tape재생을 멈추는 기능 구현*/}
      public void setCounter(int counter){this.counter = counter;}
      public int getCounter(){return counter;}
}

public interface IVCR {
      public void play();
      public void stop();
      public void setCounter(int counter);
      public int getCounter();
}


public class TVCR extends Tv implements IVCR{
      
      VCR vcr = new VCR(); //VCR객체를 이용하여 메서드를 호출한다.
      
      @Override
      public void play() {
            vcr.play();
      }
      @Override
      public void stop() {
            vcr.stop();
      }
      @Override
      public void setCounter(int counter) {
            vcr.setCounter(counter);
      }
      @Override
      public int getCounter() {
            return vcr.getCounter();
      }
}


위의 소스를 설명하자면 TVCR 클래스에 Tv와 VCR을 다중상속 한것이다. 하지만 자바에서는 상속은 하나의 클래스만 된다.
그렇기 때문에 일단 TV의 기능을 클래스로 구현하고 VCR의 기능을 클래스로 구현한다. 
그런다음 VCR에서 사용하려는 메서드들을 인터페이스인 IVCR에 정의한다.
그런 후 TVCR클래스에 Tv클래스를 상속하고 IVCR의 인터페이스를 상속한다. 
그런다음 VCR의 인스턴스를 생성하여 오버라이드해야되는 인터페이스의 메서드의 내부에서 VCR의 메서드를 그대로 호출해 준다. 위와 같은방법으로 다중상속을 하면된다.

사실 인터페이스를 새로 작성하지 않고도 VCR클래스를 TVCR클래스에 포함시키는 것만으로도 충분하다. 하지만 인터페이스를 이용하면 다형적 특성을 이용할 수 있다.
이런 인터페이스를 이용한 다형성에 대해선 바로 아래서 알아보자.


※ 인터페이스를 이용한 다형성
우리는 자손클래스의 인스턴스를 조상타입의 참조변수로 참조하는 것이 가능하다는것을 상속에서 배웠다.
ex) Parent p = new Child();

인터페이스도 이와 마찬가지 이다.
위의 코드를 예시로 들면 TVCR클래스는 IVCR 인터페이스를 상속받고 있다. 그렇기 때문에 이런것이 가능하다.
IVCR ivcr = new TVCR();
ivcr는 TVCR의 인스턴스를 참조하고 있지만 TVCR에 있는 Tv관련 멤버들은 사용하지 못하고 IVCR에 정의된 메서드 즉 TVCR에 오버라이딩 되어있는 메서드와 IVCR에 정의된 상수만 사용가능하다.
즉 상속의 다형성과 마찬가지로 인터페이스도 사용이 가능하다는 말이다.


※ 인터페이스의 장점
  1. 개발시간 단축
  2. 표준화 가능
  3. 서로 관계없는 클래스들에게 관계를 맺어줄 수 있음
  4. 독립적인 프로그래밍 가능

 - 개발시간의 단축
   인터페이스가 작성이 되면 메서드의 리턴값, 매개변수, 함수이름을 알 수 있으므로 메서드를 호출하는 쪽과 
   메서드를 구현하는 쪽, 양쪽 동시에 작업이 가능하다.

 - 표준화 가능
   프로젝트에서 사용되는 기본 틀을 인터페이스로 작성한 다음, 개발자들에게 인터페이스를 구현하여 
   프로그램을 작성하도록 함으로써 보다 일관되고 정형화된 프로그램 개발이 가능하다.

 - 서로 관계없는 클래스들에게 관계를 맺어줄 수 있음
   상속관계 혹은 같은 조상클래스를 갖고 있지 않은 서로 아무런 관계없는 클래스들에게 하나의 인터페이스를 
   공통적으로 구현하도록 함으로써 관계를 맺을 수 있다.

 - 독립적인 프로그래밍 가능
   클래스와 클래스간의 직접적인 관계를 인터페이스를 이용하여 간접적인 관계로 변경하면, 한 클래스의 변경이
   관련된 다른 클래스에 영향을 치치지 않는 독립적인 프로그래밍이 가능하다.
  
ex)

interface I {
    public abstract void method();
}    

class B implements I {

    @Override
    public void method(){
        System.out.println("[class B] method");
    }
}

class A {
    public void callClassB_Method( I i ){
        i.method();
    }
}

위와 같이 보면 클래스 A와 B 사이에는 인터페이스 I가 간접적으로 연결되어 있다. 즉 A-I-B의 관계가 되었다.
A는 단지 인터페이스에 정의된 메서드를 호출만 하면되고 B는 인터페이스에 정의된 메서드를 구현만 하면된다.
그렇기 때문에 클래스 B의 변경사항은 클래스 A에게 어떠한 영향도 주지 않는다.
또한 클래스A는 실제로 사용되는 클래스 B에대해 몰라도 되고 클래스 B가 존재하지 않아도 문제되지 않는다.
클래스 A는 오직 직접적인 관계에 있는 인터페이스 I의 영향만 받게 된다.
즉 클래스 B는 인터페이스 I에 감싸져있고 클래스 A는 인터페이스라는 껍데기 안에 어떤 알맹이(클래스)가 있는지
몰라도 된다.