Java - 인터페이스, instanceof

김지원·2022년 8월 2일
0

JAVA 총 정리

목록 보기
6/11
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 타입으로 형변환 될 수 있다면, 그 객체는 반드시 fly()라는 메서드를 구현하고 있음을 보증받는다.

상속을 받을 때 부모 객체를 자식들이 받을 수 있는 것처럼
구현하는 인터페이스로도 객체를 받을 수 있다. 그렇지만 부모 자식 관계는 아니다!!


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를 구현받고 있는가에 대한 여부를 확인 해보자.

instanceof

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
  • 레퍼런스타입은 주소값을 비교해야함.
profile
Software Developer : -)

0개의 댓글