코드 그라데이션
<보충> Day26. 인터페이스, 추상화 예제 복기하면서 주석 달기 본문
소스코드
interface Repairable{}
interface Heal{}
class Unit{
int attack;
int hitPoint;
final int Max_HP;
Unit(int hp, int attack){
Max_HP = hp; // 타고 올라오니까 처음에 150들어가고
this.attack = attack; // 10들어온다.
}
}
class GroundUnit extends Unit{
GroundUnit(int hp, int attack){
super(hp, attack);
}
}
class AirUnit extends Unit{
AirUnit(int hp, int attack){
super(hp,attack);
}
}
class Tank extends GroundUnit implements Repairable{
Tank(){ // 부모 거니까, hp, attack을 둘 다 받아줘야.
super(150,10);
hitPoint = Max_HP; // 처음에는 150 들어가있겠지.
}
// Object 메소드를 오버라이딩
public String toString() {
return "Tank"; // 디폴트값은 주소 나오지만, 여기서는 객체를 넣어줬기 때문에 앞으로는 객체가 출력됨.
}
}
class SCV extends GroundUnit implements Repairable{
SCV() {
super(60, 5);
hitPoint = Max_HP;
}
void repair(Repairable r) {
if(r instanceof Unit) {
Unit u = (Unit)r;
if(u != null && u.Max_HP != u.hitPoint) {
u.hitPoint += 1;
}
}
}
}
class Marine extends GroundUnit implements Heal{
Marine(){
super(40, 6);
hitPoint = Max_HP;
}
}
public class TestAbstract {
public static void main(String[] args) {
Tank t = new Tank();
System.out.println(t);
Marine m = new Marine();
SCV scv = new SCV();
scv.repair(t); // 동작 할까요? 동작 됨.
//scv.repair(m); // 동작 할까요?
}
}
Repairable 은 '스펙' 개념에 해당한다.
여기서는 Tank가 Repairable 하다는 것(부모자식 느낌 보다는)
void repair(Reparable r) {}은
- 수리가능한 애를 받아서(인터페이스 자체를 받았음), 이 구현체 자체만 받겠다.
- 그래서 걔가 유닛인지
한편 마린은 Heal이 가능한 스펙이라고 받아들이자.
결국 마린과 탱크를 비교해보면 되는데,
scv.repair(t); // 동작 할까요? 동작 됨.
이게 되는 이유는,
repair에서 원하는 건 Repairable 타입.
코드를 보면 탱크는 Repairable을 직접 구현하게 되어 있음.
class Tank extends GroundUnit implements Repairable{}
=> 즉, Repairable이라는 상위 타입에 Tank라는 하위 타입이 담길 수가 있는 것.
그래서 t가 오는 건 자연스러운 일.
(코드에 접근이 가능하다.)
마린은 GroundUnit이든 Unit이든 Repairable과 관련된 게 없다. Heal도 Repairable과 관계가 없다.
그래서 scv.repair(m); 은 안된다고 볼 수 있음.
void repair(Repairable r) {
if(r instanceof Unit) {
Unit u = (Unit)r;
if(u != null && u.Max_HP != u.hitPoint) {
u.hitPoint += 1;
}
}
}
r instanceof Unit 이거는 실제 인터페이스의 구현체가 Unit인지 아닌지를 검사하는 것.
그런데 지금 main에서 보면
scv.repair(t); 여기에 탱크가 담김.
=> 결국 탱크가 Unit인지를 봐야 한다.
Tank를 위에서 따라가보면
Tank => GroundUnit
GroundUnit => Unit
결국 Tank는 Unit이라고 할 수가 있음.(부모타입)
그런데 지금 r은 Repairable 타입이다.
repair을 하는 목적은 hp를 올리기 위해서임.
그런데 코드 위에 따라가보면, hp는 Unit만 가지고 있는 정보이다.
결국 hitPonit랑 Max_HP를 가지고 오려면 Unit이 되어야 하니까...
Repairable은 Unit이 아니기 때문에 얘를 강제로 Unit으로 형변환 해준 것.
Unit u = (Unit)r;
이건 직접적인 연관이 없는 것을 강제 형변환 하는 꼴이기 때문에 형변환에 대한 명시가 반드시 필요할 것.
그래서 이제는 Unit이 되었으니까, Max_HP와 hitPoint에 접근이 가능한 것.
if(u != null && u.Max_HP != u.hitPoint) {
u.hitPoint += 1;
}
주의해야 할 점은,
받을 타입은 Repairable r
그런데 Repairable과 Unit은 아무 관련도 없다.
어떻게 Repairable -> Unit이 가능했느냐.
분리해서 보면 되는데,
void repair(Repairable r)
받을 때는 이것이 Repairable이라는 인터페이스를 구현했느냐만 보면 된다.
(Tank의 상위에 Repairable이 있느냐)
=> 탱크는 Repairable을 구현했기 때문에 여기에 담길 수가 있게 됨.
일단 여기서는 그것만 보자.
다음으로 따로 살펴봐야 하는 부분은
if (r instanceof Unit) 여기인데
여기서 보면 되는 부분은
Tank가 실제로 Unit의 자식이냐. 이것임.
둘은 별개.
어떻게 이 두 과정이 모두 가능하냐
탱크가 두 가지 역할을 하고 있기 때문이다.
해답은 여기!
class Tank extends GroundUnit implements Repairable {}
탱크는 Unit이면서 , Repairable 하기 때문이다.
결국 A가 참이냐 Ok, B가 참이냐 Ok
그럼 받을 때 A로 받았으니까, (Repairable r)
이걸 가져다가 다시 B로 바꾼 꼴이다. (Unit)r
그래야만 hitPoint를 올릴 수 있으니까.
"태그처럼 사용할 수 있다"
=> 즉 출입자격을 거르는 일종의 출입증 같은 느낌.
=> 자격이 있는 애들만 여기로 들어와서 사용할 수 있어...!(사용가능여부 필터링)
들어와서 얘를 Unit인지 검사하는 건 완전히 별개의 영역이다.
'Java > Mega' 카테고리의 다른 글
Day27. 스타크래프트 문제 코드 비교 (0) | 2023.04.28 |
---|---|
<보충> 컬랙션 추가 공부(1) List, Stack (0) | 2023.04.28 |
Day31. 쓰레드 예시문제 1. (0) | 2023.04.26 |
Day31. Thread(쓰레드) (4) 쓰레드 사이의 통신 (0) | 2023.04.26 |
Day30-31. Thread(쓰레드) (3) Synchronized (0) | 2023.04.26 |