Zeolot : 땅 / 지상공격
Marine : 땅 / 공중공격
Zergling : 땅 / 지상공격
Wraith : 하늘 / 공중공격, 지상공격
날아다닌다는 속성을 부여할 것인데 이렇게 만들면 힘들다.
벤타이어그램 (아래로 갈수록 속해져있음)
Marine
--------------------
Unit
--------------------
object
Zealot
--------------------
Unit
--------------------
object
Wraith
------------------- → interface
// FlyingUnit
--------------------
Unit
--------------------
object
Wraith 유닛 추가 (marine / zeolot과의 차이는 Wraith는 날아다닌다.)
고조부모 증조부모 조부모 부모 자식
인터페이스를 사용하면 위와같은 일렬적인 관계에서 특정한 친구를 뽑아서 속성을 줄 수 있다.
- 왜 상속관계를 안쓰나면 자식이 여러개의 부모를 가질 수 없어서 1:1관계이다. 그렇지만 인터페이스는 하나의 클래스에 여러개를 사용할 수 있다.
- Wraith라는 Terran 종족 소속인 유닛 한개 생성 (Unit 상속받는다.)
어떠한 유닛은 날아다닌다라는것을 명시해주기 위해 IFly인터페이스 생성하자.
: 클래스간 상속은 1:1 이기 때문에 어떠한 클래스에 속성을 부여하여 다형성을 보다 강화시키고자 사용함.
: 대문자
I
+ 파스칼 케이스(주로 형용사), 혹은 그냥 파스칼 케이스(-able)
- 대문자
I
는 인터페이스를 알리기위해 사용하는데 안쓰는 곳도 있다.
- 접근 제한자는 반드시
public
. 생략해도public
이며 기본적으로 생략한다.- 정적인 상수. 생략해도
static final
이며 기본적으로 생략한다.- 멤버 변수는 반드시
public static final
이다.
public static final
붙이니 회색으로 변했다. 기본적으로 붙기 때문에 사용하지 않아도 된다는 의미이다.
접근제한자는 반드시
public
, 생략해도public
이며 기본적으로 생략한다.
Modifier 'protected' not allowed here
: 인터페이스는 protected 접근 제한자를 허용하지 않는다.
- 접근 제한자를 명시하지 않는다고 해도 위 메서드의 접근 제한자는 default가 아니다. 인터페이스가 가지는 모든 접근 제한자는 public이기 때문에 생략하는 것이다.
기본적으로 추상적이다.(구현부를 가지지 않는다.)
Interface abstract methods cannot have body
: 인터페이스 추상 메서드는 구현부를 가질 수 없다.- 기본적으로 추상적(구현부를 가지지 않는다.) 생략해도 abstract이며 기본적으로 생략한다.
implements 인터페이스 이름,...
- 인터페이스는 콤마로 구분하여 여러개 구현할 수 있음. (인터페이스만!)
Class 'Wraith' must either be declared abstract or implement abstract method 'fly()' in 'IFly'
이 클래스를 IFly 인터페이스를 구현하게 하고 싶으면 IFly가 가진 fly()라는 추상메서드를 구현하거나 Wraith가 추상클래스여야한다. 라는 오류가 뜬다.
Wraith가 추상적이게 되면 객체화를 할 수 없다.
Wraith wraith = new Wraith();
그렇기 때문에 fly()메서드를 재정의하자!
메인으로 넘어와서 Wraith 객체화하고 호출해보자.
- toString을 재정의 했기 때문에
[TERRAN] 레이스
이런 형태로 출력이 된다.
부모로 부터 자식객체를 받을 수 있기 때문에 Wraith 객체를 오브젝트 타입, 유닛 타입으로 받을 수 있다.
- Unit은 attack 메서드를 가지는데 objWraith로는 범위가 제한되있기 떄문에 호출하지 못한다.
- 인터페이스도 객체를 가질 수 있다.
- fly만 있고 attack이 없다.
상속을 받을 때 부모 객체를 자식들이 받을 수 있는 것처럼
구현하는 인터페이스로도 객체를 받을 수 있다. 그렇지만 부모 자식 관계는 아니다!!
IFly flyMarine = marine;
ClassCastException
: 클래스 형변환 예외 발생- marine 객체의 타입인 Marine은 IFly 인터페이스를 구현하지 않음으로 IFly 타입으로 자동 형변환 될 수 없다.
Unit클래스로 가서 attack메서드에 공격 대상 기능을 추가하자.
유닛만 공격 대상이 될 수 있도록 하자.
- Unit을 상속받아 attack메서드를 구현하고 있는 클래스 모두 같이 수정해줘야함.
- %s는 문자열을 나타내기 위해 존재한다고 헀는데 this와 target은 둘 다 stringd이 아니다.
모든 레퍼런스 타입을 String 타입으로 변환하는 방법에 toString이 있다.
%s자리에 문자열이 아닌 것이 들어가게 되면 객체의 toString을 호출한다.
this.toString() / target.toString() 이렇게 쓰며 생략가능하다.
유닛의 이름만 알고 싶다면 this.getName() 으로 작성하면 된다.
getName은 유닛이 가지고 있기 때문에 this(나), target도 호출이 가능한 것이다.
- 질럿은 레이스(날아다니는 유닛)를 공격하지 못해야하는데 공격을 할 수 있게 되있다. 막아줘야한다.
Wraith, Marine, Zealot에서 다 똑같은 attack의 구현부를 가지기 때문에 추상적으로 만들어가면서까지 구현을 다 할 필요없다.
- Unit 클래스에서 attack메서드의 접근 제한자를 abstract에서 defalut로 바꿈으로써 딱 한번만 구현하게 된다.
- 즉, Wraith, Marine, Zealot 에서 재정의하지 않아도 된다.
Zeolot이 공중공격할 수 없도록 해보자.
- attack 메서드 재정의
super.attack(target);
: 메서드를 재정의했을 때 타입을 어떤 것으로 바꾸든 재정의 된 메서드가 호출이 되었다.
재정의되기 전 부모의 내용을 호출할 수 있는 유일한 방법은 super을 통하여 메서드를 직접호출하면 된다.
Zeolot이 target(공격대상)으로 하여금 공격할 수 있는 대상인가?
target이 날아다니는가에 대한 여부를 먼저 따지자.
전달받은 Unit이 IFly를 구현받고 있는가에 대한 여부를 확인 해보자.
if(객체 instanceof 타입) { }
: 객체가 명시한 타입으로 형변환 될 수 있는가? 에 대한 여부 반환
- target 객체가 IFly 타입으로 형변환 될 수 있는가? 에 대해 안전하게 물어보는 것이다.
( 억지로 형변환해서 오류터트려서 예외를 막는 방법도 있지만 instanceof을 훨씬 훨씬 많이 쓴다.)- 앞에 객체가 와야하고 뒤에 타입이 와야한다.
- boolean타입으로 반환이 되고 따로 변수로 빼서 사용할 수도 있지만 이렇게 안한다.
- target이 IFly타입으로 형변환될 수 있다면 해당 공격대상은 날아다니다는 것임으로 공격할 수 없게 하면 된다.
어떠한 유닛이 공격을 할 때 공격 대상이 날아다니고 공격을 하는 대상이 공중공격을 못하면 막아야한다.
- Zergling 생성 (날아다닌다.)
- Zeolot에 attack 메서드를 재정의한 것처럼 Zergling에 attack 메서드 재정의 하지 않아서 이런일이 발생했다.
- 이런 재정의를 일일히 다 해줄수가 없기 때문에 인터페이스를 사용한다.
지상공격을 할 수 있다. 공중공격을 할 수 있다. 라는 건 Unit의 속성이다.
공중공격을 할 수 있다라는 인터페이스를 만들자.
- 이 인터페이스를 가진 유닛을 공중공격을 할 수 있다 라는 걸 알려준다.
- IAttactAir가 추상메서드를 하나도 안가지고 있기때문에 재정의 할 게 없다.
공격 대상인 target이 공중유닛(IFly)이고 내가 공중공격을 할 수 없음(!~IAttackAir) ::: 공격하면 안됨
target instanceof IFly && !(this instanceof IAttackAir)) 공격대상이 공중유닛이고 내가 공중공격 속성을 가지고 있지 않을 때
-> 질럿도 저글링도 attack 메서드를 재정의를 할 필요 없다. (삭제) 위의 attack메서드에서 다 해결이 된다.
marine.attack(wraith);
wraith : IFly && marine : IAttackAir
wraith.attack(wraith);
wraith : IFly && wraith : IAttackAir
zealot.attack(wraith);
wraith : IFly && zealot : IAttackAir (x)
zergling.attack(wraith);
wraith : IFly && zergling : IAttackAir (x)
zealot.attack(zergling); wraith : IFly (x)
공격 대상과 자기 자신이 "같은 객체" 일때 자살 방지를 하자
- 받아온 target과 this가 같다면 죽이지 못하게끔 해주면 된다.
marine.attack(marine); ↑ this ↑ target
- 레퍼런스타입은 주소값을 비교해야함.