본문 바로가기

플밍 is 뭔들/JAVA

[자바] 인터페이스와 상속의 비교 및 정리

※ extends와 implements의 차이

 - extends
상속을 뜻한다. 사전적 의미로는 확장,확대의 뜻을 가지고 있다. 
코딩으로 치면 조상의 멤버(메서드, 변수 등)를 자식에서 그대로 물려받아 추가적으로 자식의 멤버를 추가, 즉 조상의 멤버를 물려받고 자식에서 거기에 추가로 확장하여 사용하는 의미로 보면 적당할 것 같다.
따라서 자식으로 내려갈 수록 조상보다 멤버의 범위가 더 넓다.

클래스끼리는 하나의 클래스만 상속만 된다.
public class Ambulance extends Car{}

인터페이스끼리는 다중 상속이 가능하다. 이렇게 여러개의 인터페이스를 상속받은 인터페이스를 클래스에 적용할 땐 모든 메서드를 구현해 줘야한다.
interface InterfaceTest1 {
      public final int a =110;
      public void method1();
}

public interface InterfaceTest2 {
      public void method2();
      public void method3();
}

public interface InterfaceTest3 extends InterfaceTest1, InterfaceTest2{}  <--인터페이스 다중상속

public class InterfaceTestClass implements InterfaceTest3{
      int b = a; //여기서 a는 InterfaceTest1의 멤버 변수 a 이다.
      
      //InterfaceTest1,2,3의 모든 메서드를 구현해줘야 한다.
      @Override
      public void method1() { /* 구현 */
            System.out.println(a);
      }
      @Override
      public void method2() { /* 구현 */
            System.out.println(a);}

      @Override
      public void method3() { /* 구현 */
            System.out.println(a);  }

}


 - implements 
사전적 의미로 도구, 이행하다란 뜻을 가지고 있다.
확장의 이미보단 인터페이스에 정의된 멤버들을 클래스에서 대신 구현을 하겠다는 의미로 보면 적당할 것 같다.
여러개의 인터페이스를 받아 메서드를 구현 할 수 있다.
public class InterfaceTestClass implements InterfaceTest1, InterfaceTest2{ /* 구현 */ 


※ 상속과 인터페이스를 이용한 다중상속
자바에서는 다중상속이 불가능 하다. 하지만 인터페이스를 이용하여 다중 상속처럼 사용할 수 있다.

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();
      }
}

우리는 VCR클래스와 TV클래스, 두 클래스를 동시에 상속해 TVCR이라는 클래스를 만들려고 한다. 하지만 자바에서는 말했다시피 다중상속이 안된다. 그렇기 때문에 인터페이스를 이용하여 다중상속처럼 만들었다.
방법의 순서는 아래와 같다.

  1. VCR에서 사용되는 메서드를 그대로 이름을 따와 IVCR이라는 인터페이스를 만들었다.
  2. 최종적으로 구현하려는 TVCR클래스에 TV클래스를 상속하고 IVCR이라는 인터페이스의 메서드를 TVCR클래스에서 구현하겠다고 implements로 선언했다.
  3. VCR클래스의 인스턴스를 생성하여 오바라이딩된 IVCR의 메서드에서 VCR의 메서드를 호출한다.
위와 같이 만들면 TV클래스와 VCR클래스의 모든 메서드와 멤버를 사용할 수 있다. 즉 다중상속과 같아진다.


※ 인터페이스와 상속의 다형성 비교
그럼 일단 인터페이스의 다형성에 대해 알아보기전에 상속과 인터페이스의 다형성에대해 잠깐 비교해보자.
상속의 다형성의 기본형태는 부모클래스 p = new 자식클래스() 이다. 부모클래스p가 참조할 클래스는 자신의 클래스보다 범위가 넓거나 같아야 한다.
즉 아래 소스처럼 부모클래스 자신을 참조하거나 자신보다 범위가 넓은 자식클래스를 참조해야한다.

class Car {/*구현*/}

class FireEngine extends Car {/*구현*/}

public static void main(String[] args) {

    /*자기 자신을 참조하여 객체 생성*/
    Car c = new Car();

    /*자기 자신보다 범위가 넓은 자식클래스를 참조하여 객체 생성*/
    Car c1 = new FireEngine();
    
    /*형변환 하여 객체 생성*/
    FireEngin f = (FireEngine)new Car();

}

단 c1객체의 경우 FireEngine()을 참조하고 있지만 Car의 인스턴스이므로 Car클래스에 정의된 멤버만 사용이 가능하다.
f객체의 경우 Car()를 참조했지만 FireEngin()으로 형변환을 했으므로 FireEngin클래스의 메서드를 사용할 수 있다.
f는 형변환을 하지 않으면 에러가 난다.
참고로 c는 Car의 인스턴스이고 c1은 Car의 인스턴스이자 FireEngine의 인스턴스이다.

이제 인터페이스의 경우에서의 다형성을 보자
public class InterfaceTestClass implements InterfaceTest1 , InterfaceTest2, InterfaceTest3{/*구현*/}

public static void main(String[] args) {
    InterfaceTest1 i1 = new InterfaceTestClass();
    InterfaceTest2 i2 = new InterfaceTestClass();
    InterfaceTest3 i3 = new InterfaceTestClass();
}


위에서 보다시피 InterfaceTest1,2,3는 인터페이스이다. 그리고 InterfaceTestClass는 클래스이다.
즉 인터페이스 i = new 클래스()와 같은 형태로 인터페이스 객체를 만들어 다형성을 사용할 수 있다.
아마 InterfaceTestClass에서는 InterfaceTest1,2,3의 모든 메서드들이 구현되어 있을것 이다. 
하지만 i1에서는 InterfaceTest1의 메서드만 사용가능하고 i2는 InterfaceTest2의 메서드만 사용이 가능하고 i3은 InterfaceTest3의 메서드만 사용이 가능하다.


※ 인터페이스의 다형성 사용 예시
상속의 다형성의 사용법은 아래 다형성과 상속의 글에서 썻기 때문에 자세한 설명은 하지 않겠다.
그렇다면 이러한 인터페이스의 다형성을 이용하여 무엇을 할 수 있을까?
스타크래프트를 예로 들어보자. 
SCV, 탱크, 레이스, 마린 네가지의 유닛이 있다고 하자. 이 네 유닛은 기본적인 유닛의 특성을 정의해 놓은 멤버가 있는 Unit이라는 클래스를 상속받아  구현된다 하자.
여기서 SCV의 수리기능은 탱크와 레이스에는 사용할 수 있지만 마린에는 사용할 수 없다. 
그렇다면 SCV의 수리 메서드는 아래와 같이 만들어야 할 것이다.

void Repair(Tank t){
    //수리 기능
}

void Repair(Wraith w){
    //수리기능
}

하지만 이런식으로 만들면 Repair메소드가 엄청나게 많아질 것이다. 그렇다고 상속의 다형성을 이용하기에는 개념적으로 애매한 면이 있다.
탱크와 마린, 레이스는 모두 유닛이라는 클래스를 상속받아야 하기 때문에 수리가 되고 안되는 것을 구분할 기준이 없다.
그래서 인터페이스를 사용해서 아래와 같이 탱크와 레이스, 마린의 클래스를 정의해보자.

public class Unit {
      
      public String name;
      
      public void move(){
            System.out.println("이동");
      }
}

public class Wraith extends Unit implements Reparable{
      
      public Wraith() {
            name = "Wraith";
      }
      
      public void attack(){
            System.out.println("뿅뿅");
      }
}

public class Tank extends Unit implements Reparable{
      
      public Tank() {
            name = "Tank";
      }
      
      public void attack(){
            System.out.println("퉁퉁");
      }
}

public class Marine extends Unit{
      public Marine() {
            name = "Marine";
      }
      
      public void attack(){
            System.out.println("두두");
      }
}

public class Scv extends Unit implements Reparable{
      
      public void Repair(Unit u){
            
            if( u instanceof Reparable){
                  System.out.println(u.name + " 지이잉 수리");
            }else{
                  System.out.println(u.name + " 수리할수 없는뎁쇼?");
            }
      }
}

Reparable인스턴스를 만들어 수리 가능한 유닛에 implements로 정의해 두었다. 그리고 Repair 메서드에서 Unit객체를 받아 Reparable이 포함되어 있는지 없는지를 구분하여 수리할지 말지를 정의한다.
그럼 메인클래스에서 실행을 해보자.

public class Main {
      
      public static void main(String[] args){
            Scv scv = new Scv();
            Tank tank = new Tank();
            Wraith wraith = new Wraith();
            Marine marine = new Marine();
            
            scv.Repair(tank);
            scv.Repair(wraith);
            scv.Repair(marine);
            
      }
}

결과는 다음과 같다.
Tank 지이잉 수리
Wraith 지이잉 수리
Marine 수리할수 없는뎁쇼?

마린을 구분하여 수리할 수 없다고 나온다.
즉 이런식으로 인터페이스와 상속의 다형성을 이용하면 코드가 좀더 간결하고 깔끔해 질 수 있다.




'플밍 is 뭔들 > JAVA' 카테고리의 다른 글

[자바] 네트워킹 (Networking)  (0) 2017.09.28
[자바] 쓰레드(thread)  (0) 2017.09.25
[자바] 예외처리 (Exception)  (0) 2017.09.19
[자바] 인터페이스 (Interface)  (0) 2017.09.19
[자바] 추상클래스 (Abstract Class)  (0) 2017.09.18