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 , 탱크 , 벌쳐 로 묶어 줄수 있다.
따라서
끝으로 간단하게 짜본 코드는
interface로 크게 종족(테란,저그,프로토스)를 묶고 또 따로 다른특징들(Repairable)을 묶어준다. 이후
각 종족별로 abstract class을 만들어 준후에 개별적으로 매서드를 만들어준다
이후 각 종족별 전투병들의 특징을 class로 만들어 주면된다.
어떻게 설명 해볼려고 코드를 어거지로 짠감이 없지않아 있지만 부디 도움이 됬음 좋겠다
자바공부<6> - 컬렉션 프레임웍 (List,Set,Map,Hashing) (0) | 2018.07.17 |
---|---|
parseDouble과 parseInt의 구조적 차이 (판정 유무) (1) | 2018.07.12 |
자바공부<4> - 다형성 (0) | 2018.07.09 |
자바공부<3> - 예외처리 (1) | 2018.07.03 |
자바 공부<2> - 상속,캡슐화,오버라이딩,오버로딩,final,static,this(),super() (0) | 2018.07.02 |