상세 컨텐츠

본문 제목

자바공부<5> - 인터페이스

JAVA

by oimb 2018. 7. 10. 10:49

본문



1. 인터페이스


인터페이스를 하기에 앞서 다형성에 대해서 어느정도는 알고 있어야한다.(http://history1994.tistory.com/11?category=668240)



보통 교과서를 보면 주로 인터페이스를 정리하는 단어로 '기본 설계도' 라고 한다.  이는  인터페이스의 구성 요소들 때문에 붙여진 이름 같다.

인터페이스는 오직 추상 메서드와 상수만을 멤버로 가질 수 있다.


interface 객체이름{

public static final  상수이름 = 값 ;  // 모든 멤버변수는 public static final 이어야 한다. (생략가능)

public abstract 메서드이름 (매개변수...) ; //  모든 메서드는 public abstract 이어야 한다. (생략가능)


}

이러한 형태로 이루어져 있다.  그리고 위의 주석에 대한 내용중 생략가능이라고 써놨는데, 이는 컴파일시 컴파일러가 자동으로 추가해주기 때문이다.


2. 특징


인터페이스의 특징에 대해 설명 하자면 첫번째로 인터페이스도 상속을 받을 수 있는데 대신 인터페이스로부터만 상속받을 수 있다. 또 클래스와는 다르게 인터페이스는 다중상속을 지원 한다. 

두 번째로 인터페이스는 그 자체로는 인스턴스를 생성할 수 없다. 따라서 다른 클래스가 이 인터페이스에 있는 멤버들을 구현해줘야만 인스턴스화 될 수 있다. 상속한다는 extends 와 비슷하지만 확장이 아닌 구현한다는 개념이기 때문에 implements 키워드를 사용 한다.


class 클래스이름 implements 인터페이스이름 {

//  인터페이스에 정의된 추상메서드를 구현해야 한다.

}


이 인터페이스를 구현한 클래스를 사용하고 싶다면 반드시 해당 인터페이스의 메서드를 전부 구현해야 한다. 만약 일부만 구현 한다면 abstract를 붙여서 추상 클래스로 선언해야한다.




3. 쓰임 & 의미


첫 번째 쓰임에 대해서 설명 하자면 일단 다중상속을 해결하는 방법이다.

자바는 다중상속을 지원하지 않는다는 단점이 존재 한다. 하지만 인터페이스를 이용하면 다중상속을 구현할 수 있다.


class A{

//A의 매서드 기술

}

class B{

public void get(){}

public void take(){}

// B의 메서드 기술

}


이 상황에서 A 와 B의 기능을 모두 갖는 클래스가 만들어야 한다고 가정하자 (class C) 즉 다중상속을 구현 해야 한다고 하자

여기서 A와  B중의 더 부분적인 기능을 갖는 클래스를 선택한다.  => B 를 선택

그리고 B와 동일한 매서드를 갖는 인터페이스를 작성한다.


public interface InterfaceB{

public abstract void get();

public abstract void take();

//B의 메서드 기술

}


class C extends A implements InterfaceB{

B b = new B();

public void get(){

b.get();

}

pulbic void take(){

b.take();

}

}


이렇게 다중 상속을 구현 할 수 있다. 물론 class C 에 class B의 모든 멤버 함수를 구현할 수도 있다. 하지만 이런식으로 하면 B의 내용이 바뀔 때마다 C의 내용도 바꿔야 하므로 좋지 못하다.


두 번째는 다형성을 제공 하여 좀더 효과적인 개발을 제공하는 점이다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.CharBuffer;
 
public class test {
    public static void main(String[] args) {
        SCV scv= new SCV(150);
        Tank tank = new Tank(250);
        Vulture vulture = new Vulture(200);
        tank=(Tank)scv.repair(tank);
        vulture=(Vulture)scv.repair(vulture);
        scv.hitSkill();
        scv.move();
    }    
}
interface Repairable{}
interface Unit{    
    public abstract void move();
    void attack();        
}
abstract class Terran implements Unit{
    
    public final int MAX_HP;
    abstract void hitSkill();    
 
    public Terran(int hp) {
        MAX_HP = hp;
    }        
    @Override
    public void move() {
        System.out.println("이동");
    }
    @Override
    public void attack() {
        System.out.println("어택");        
    }
}
 
class SCV extends Terran  implements Repairable{
    
    public SCV(int hp) {
        super(hp);    
    }
 
    public Repairable repair(Repairable u) {
        if(u instanceof Tank) {
            System.out.println("탱크수리중");
            System.out.println("탱크수리완료");
            return u;
        }
        else if(u instanceof Vulture) {
            System.out.println("벌쳐수리중");
            System.out.println("벌쳐수리완료");
            return u;
        }else 
            return null;
    }
 
    @Override
    void hitSkill() {
        attack(); // 기본공격
    }
}
 
 
class Tank extends Terran  implements Repairable{
 
    public Tank(int hp) {
        super(hp);    
    }
    //탱크 내용
 
    @Override
    void hitSkill() {
        // 공격 방법을 기술 탱크는 시저 모드
        attack();
        
    }
}
class Vulture extends Terran  implements Repairable{
 
    public Vulture(int hp) {
        super(hp); 
    }
    //벌쳐 내용
 
    @Override
    void hitSkill() {
        // 공격 방법을 기술 벌처는 마인
        attack();    
    }
}
cs



예시를 들려고 좀 쓸대없이 코드가 길어졌는데

 46 ~58 번째 라인을 보자 리턴형이 인터페이스 임을 볼수 있다. 이것은 의미하는 바가 크다

리턴 타입이 인터페이스라는 것은 인터페이스를 리턴한다는 뜻이 아니다. 인터페이스 참조변수가 가리키고 있는 인스턴스를 리턴한다는 뜻이다

즉 , 해당 인터페이스를 구현하는 클래스의 인스턴스를 반환한다는 뜻이다


세 번째는 직접적인 관계에서 간접적인 관계로 바꿔줄 수 있다.


위 내용과 비슷하듯 다른데  예로서 설명 하자면

class T 와 class K 가 아주 밀접한 관계가 있다고하자  


class T{

void m(K k){

k.history();   // k의 함수를 호출

}

}

class K{

void history(){}

}


에를 들면 이런식이다.  이러한 관계가 있을 때 , K의 함수 선언부가 바뀐다면 어떻게 될까? 또 아직  클래스 K를 아직 작성하지 못한 상황에서는 어떻게 될까?

class T를 작성하는데 있어 직접적인 오류가 날것이다.


하지만 이를 인터페이스를 이용해 간접적으로 접근한다면 애기는 달라진다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
public class test {
 
    public static void main(String[] args) {
 
        T t = new T();
 
        t.m(new K());
 
        t.m(new K2());
 
    }
 
}
 
interface I {
 
    public abstract void history();
 
}
 
class K implements I {
 
    public void history() {
 
        System.out.println("K");
 
    }
 
}
 
class K2 implements I {
 
    public void history() {
 
        System.out.println("K2");
 
    }
 
}
 
 
 
class T {
 
    void m(I i) {
 
        i.history(); // 인터페이스를 구현한 클래스의 history 호출
 
    }
 
}
 
 
cs

이렇게 되면 인터페이스는 인터페이스 I 와 만 직접으로 관련 있게되어지고 class K의 영향을 직접적으로 받지 않게된다. 클래스 K,K2를 전부 주석처리 해보고 run 시켜보자 에러가 나는지 그리고 


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class test {
 
    public static void main(String[] args) {
 
        T t = new T();
 
    }
 
}
 
interface I {
 
    public abstract int history();
 
}
 
class K implements I {
 
    public int history() {
 
        System.out.println("K");
        return 1;
 
    }
 
}
 
class T {
 
    void m(I i) {
 
        i.history(); // 인터페이스를 구현한 클래스의 history 호출
 
    }
 
}
cs

class K 와 interface I 를 저렇게 변경 시켜보자 그래도 class T는 변화 된것이 없다. 그리고 interface I 를 구현한 클래스의 이름을 알지 못해도 된다는 점이 있다.






4. 장점


인터페이스에 대한 일반적인 장점을 살펴보면 

1. 개발의 효율 up

2. 표준화

3. 독립적으로 프로그래밍

4. 서로 관계없는 클래스를 맺어 줄 수있다.


3. 쓰임과 의미 내용을 통해서  1번과 3번의 내용은 설명이 된다.


따라서 2번과 4번을 좀더 살펴 볼것인데



자 테란 종족의 유닛들이다

여기서 게임을 좀 해보신 분들은 유닛들의 차이를 느낄 수 있다. 마린 vs scv 탱크 벌쳐의 차이는 수리를 할수 있는가와 없는가이다.

즉 후자는 수리를 위한 reapair() 매서드를 추가해줘야 한다. 즉 scv 클래스 내에 void repair(탱크 t) , void repair(벌쳐 v) ,... 등 매개변수 별로 전부 다 추가 해줘야한다.

이 때 인터페이스를 사용하여 

4.서로 관계없는 클래스를 맺어줄 수 있다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.nio.CharBuffer;
 
public class test {
    public static void main(String[] args) {
        SCV scv= new SCV(150);
        Tank tank = new Tank(250);
        Vulture vulture = new Vulture(200);
        tank=(Tank)scv.repair(tank);
        vulture=(Vulture)scv.repair(vulture);
        scv.hitSkill();
        scv.move();
    }    
}
interface Repairable{}
interface Unit{    
    public abstract void move();
    void attack();        
}
abstract class Terran implements Unit{
    
    public final int MAX_HP;
    abstract void hitSkill();    
 
    public Terran(int hp) {
        MAX_HP = hp;
    }        
    @Override
    public void move() {
        System.out.println("이동");
    }
    @Override
    public void attack() {
        System.out.println("어택");        
    }
}
 
class SCV extends Terran  implements Repairable{
    
    public SCV(int hp) {
        super(hp);    
    }
 
    public Repairable repair(Repairable u) {
        if(u instanceof Tank) {
            System.out.println("탱크수리중");
            System.out.println("탱크수리완료");
            return u;
        }
        else if(u instanceof Vulture) {
            System.out.println("벌쳐수리중");
            System.out.println("벌쳐수리완료");
            return u;
        }else 
            return null;
    }
 
    @Override
    void hitSkill() {
        attack(); // 기본공격
    }
}
 
 
class Tank extends Terran  implements Repairable{
 
    public Tank(int hp) {
        super(hp);    
    }
    //탱크 내용
 
    @Override
    void hitSkill() {
        // 공격 방법을 기술 탱크는 시저 모드
        attack();
        
    }
}
class Vulture extends Terran  implements Repairable{
 
    public Vulture(int hp) {
        super(hp); 
    }
    //벌쳐 내용
 
    @Override
    void hitSkill() {
        // 공격 방법을 기술 벌처는 마인
        attack();    
    }
}
cs



17번째 줄 Repairable 인터페이스를 이용해서 각 클래스  scv , 탱크 , 벌쳐  로 묶어 줄수 있다. 

따라서 

 public Repairable repair(Repairable u) {
        if(u instanceof Tank) {
            System.out.println("탱크수리중");
            System.out.println("탱크수리완료");
            return u;
        }
        else if(u instanceof Vulture) {
            System.out.println("벌쳐수리중");
            System.out.println("벌쳐수리완료");
            return u;
        }else 
            return null;
    }
와 같이 Repairable 형 매개 변수로 전부 받을수 있다.
아래 instanceof 키워드는 사실 없어도 된다. 그냥 학습용으로 구분 지은것 뿐



끝으로 간단하게 짜본 코드는


interface로 크게 종족(테란,저그,프로토스)를 묶고 또 따로 다른특징들(Repairable)을 묶어준다. 이후

각 종족별로 abstract class을 만들어 준후에 개별적으로 매서드를 만들어준다

이후 각 종족별 전투병들의 특징을 class로 만들어 주면된다.



어떻게 설명 해볼려고 코드를 어거지로 짠감이 없지않아 있지만 부디 도움이 됬음 좋겠다








관련글 더보기