인터페이스
지난 포스팅에서 추상화에 대해 언급했다면 오늘은 추상화보다 더 심각하게 추상적인 인터페이스에 대해 정리한다.
인터페이스란 간단히 정리하자면 객체의 사용 방법을 정의한 타입이다. 또한 객체의 교환성을 높여주기 때문에
다형성에 매우 중요한 역활을 차지한다. 자바8 에서는 기능이 추가되어 람다식에 대한 기능까지 추가되며 중요성이
더욱 커지게 되었다.
※ 이번 정리에서 언급할 인터페이스 내용 중에서 디폴드 메소드와 람다식에 대해서는 아직 내용을 배우지 못하였다.
따라서 이번 인터페이스에서는 기본 개념만 언급하며 추후 익명 인터페이스 등 다양한 방면으로 여러번에 걸쳐 정리하고자 한다.
1. 인터페이스의 장점
1) 코드 수정이 필요없다.
인터페이스는 개발 코드와 객체가 서로 통신하는 접점 역활을 한다. 개발 코드가 인터페이스의 메소드를 호출하면
인터페이스는 객체의 메소드를 호출시킨다. 이런 특징을 이용하면 개발 코드를 수정하지 않아도, 사용 중인 객체를
변경할 수 있게 된다.
2) 여러 객체에 대해 이식이 편리하다.
인터페이스는 여러 객체들과 사용이 가능하여 객체의 내용에 따라 실행 내용과 리턴값이 다르게 된다.
따라서 코드상에서는 코드의 수정 없이도 실행 내용과 리턴값을 다양하게 사용할 수 있다.
2. 인터페이스 선언 / 정의
인터페이스는 ~.java 의 형태로 작성되며 ~.class 형태로 컴파일되어 물리적으로는 클래스와 비슷하지만 선언하는
방법은 다르다.
[접근 지정자] interface 이름 { ... }
interface Fightable {
public void attack(Unit u);
}
메소드를 살펴보면 추상화와 비슷하게 메소드의 이름과 반환형, 시그니처는 선언되었으나 기능에 대한 정의가 전혀
이루어지지 않았다. 하지만 abstract 또한 붙어있지 않다. 이 점을 잘 봐두자.
3. 인터페이스의 멤버
인터페이스의 특징은 추상 클래스처럼 인스턴스를 만들 수 없다. 하지만 추상클래스는 생성자가 존재하지만
인터페이스는 생성자가 존재하지 않는다.
이처럼 일반적인 클래스와 유사한 형태를 가졌으나 멤버로 가질 수 있는 종류는 많지 않다.
1) 상수
=> 상수는 인터페이스에 고정된 값으로 런타임 중에는 값을 바꿀 수 없기에 사용이 가능하다.
상수를 사용하기 위해서는 선언 시 반드시 초기값을 넣어야 한다. public 이 기본 형이며 생략 가능하다.
2) 추상 메소드
=> publc abstract 의 기본 특성을 갖기 때문에 publc abstract을 생략하더라고 컴파일시 자동으로 붙는다.
3) 디폴트 메소드 (자바 8 부터 추가)
=> default 가 리턴 타입 앞에 붙는다. public 이 기본 특성이기에 public 을 생략 가능하다.
4) 정적 메소드 (자바 8 부터 추가)
=> 일반적인 클래스 메소드와 형태가 동일하다. 기본 형태는 public 이며 생략 가능하다.
interface Member {
// 1. 상수
final int final_Num = 10;
// 2. 추상 메소드
public void Add(int num1, int num2);
// 3. 디폴트 메소드
default void minus(int num1, int num2) {
//메소드 내용까지 정의 가능!
int result = num1 - num2;
}
// 4. 정적 메소드
static void multiple(int num1, int num2) {
//메소드 내용까지 정의 가능!
int result = num1 * num2;
}
}
4. 인터페이스 직접 사용해보기
우선 메소드를 클래스와 사용하기 위해서는 implements 예약어를 알아야 한다.
implement 는 "실행하다" 라는 뜻을 갖는 단어이다.
즉, 클래스에서 다음 인터페이스를 실행한다. 라는 의미를 담아 implements 의 뒤에 인터페이스 이름을 넣는다.
또한 클래스는 상속과 달리 여러 인터페이스를 다중으로 사용이 가능하다.
public class Childclass extends Parentclass implements Interface1 , Interface2 {
// 인터페이스 내용
}
이번 수업시간에 인터페이스 사용방법을 익히기 위해 스타크래프트 유닛을 코드로 구현하여 봤다.
우선 어떤 기능을 갖는지 정의한 인터페이스 코드이다.
// 인터페이스 : 클래스의 그룹화
interface Fightable {
public void attack(Unit u);
}
interface Mechanic {
public void repair(Unit u);
}
우선 Fightable 인터페이스는 공격 가능 기능을 갖으며 attack 추상 메소드를 갖는다.
Mechanic 은 수리가 된다는 특징을 갖는 reapir 추상 메소드를 갖는다.
<Unit Class>
가장 처음으로 유닛의 기본 정보를 갖는 부모 클래스이다. 또한 형태는 추상 클래스이다.
public abstract class Unit {
private int x;
private int y;
private int hp;
private int maxHp;
public Unit(int maxHp) {
this.hp = maxHp;
this.maxHp = maxHp;
}
public Unit dead(Unit u) {
System.out.println(u + "사망!");
u = null; // 사망한 유닛에 null을 넣어 소멸
return u;
}
// 유닛별로 이동 형태가 다르기 때문에 추상 메소드로 정의
public abstract void move(int x, int y);
public void stop() {
System.out.println(x + " , " + y + "에 정지");
}
public int getHp() {
return hp;
}
public void setHp(int hp) {
this.hp = hp;
}
public int getMaxHp() {
return maxHp;
}
public void setMaxHp(int maxHp) {
this.maxHp = maxHp;
}
}
<Marine Class>
다음은 테란의 마린이다.
마린은 지상 유닛이며 공격이 가능하다. 따라서 공격이 가능하도록 공격 기능을 선언한 인터페이스를 붙인다.
// 마린 클래스
class Marine extends Unit implements Fightable { // 공격에 대한 인터페이스를 붙인다.
int attackSpeed = 10; // 공격 속도
@Override
public Unit dead(Unit u) {
super.dead(u);
return u;
}
public Marine(int hp) {
super(40);
}
@Override
public void move(int x, int y) {
System.out.println(x + " , " + y + "로 뛰어서 이동");
}
public void stimpack() {
attackSpeed += 5;
int hp = super.getHp();
hp -= 10;
super.setHp(hp);
}
@Override
public String toString() {
return "마린!";
}
@Override
public void attack(Unit u) {
int hp = u.getHp();
hp -= 200; // 공격력 : 2 , 대상 유닛의 Hp를 2 씩 감소
u.setHp(hp);
System.out.println(u + "공격!");
if(u.getHp() <= 0 ) {
u.dead(u);
}
}
}
코드를 보면 추상화된 부모 클래스에서 메소드를 상속받아 사용하는 것을 볼 수 있다.
중요한 것은 attack() 메소드이다. 부모 클래스에 없는 attack() 은 위에서 본 인터페이스에서 가져온 메소드임을
알 수 있다.
<Tank Class>
다음은 테란의 상징인 시즈탱크의 코드이다.
시즈탱크는 마린처럼 공격이 가능하지만 수리가 가능한 메카닉 유닛이다.
이에 Fightable 인터페이스와 Mechanic 인터페이스를 다중으로 사용하는 것을 볼 수 있다.
// 탱크 클래스
class Tank extends Unit implements Fightable, Mechanic {
boolean mode = false; // F : 탱크모드 , T : 시즈모드
public Tank(int hp) {
super(hp);
}
@Override
public void move(int x, int y) {
System.out.println(x + " , " + y + "로 굴러서 이동");
}
public void changeMode() {
mode = !mode;
}
@Override
public String toString() {
return "시즈탱크";
}
@Override
public void attack(Unit u) {
int hp = u.getHp();
hp -= 30; // 공격력 : 30 , 대상 유닛의 Hp를 30 씩 감소
u.setHp(hp);
System.out.println(u + "공격!");
}
@Override
public void repair(Unit u) {
int hp = u.getHp();
while(u.getHp() < u.getMaxHp()) {
u.setHp(hp += 1);
}
}
}
마린과 마찬가지로 추상화된 부모클래스와 인터페이스의 메소드를 오버라이딩한 것을 볼 수 있다.
<DropShip Class>
다음은 수송선인 드랍쉽의 코드이다.
드랍쉽은 수송은 가능하지만 공격 기능이 없다. 또한 메카닉 이므로 수리가 가능하다.
따라서 Mechanic 인터페이스를 사용한다.
class Dropship extends Unit implements Mechanic {
public Dropship(int hp) {
super(hp);
}
@Override
public void move(int x, int y) {
// TODO Auto-generated method stub
System.out.println(x + " , " + y + "로 날아서 이동");
}
public void load() {
System.out.println("수송!");
}
public void unload() {
System.out.println("하역!");
}
@Override
public String toString() {
return "드랍쉽";
}
@Override
public void repair(Unit u) {
int hp = u.getHp();
while(u.getHp() < u.getMaxHp())
u.setHp(hp += 1);
}
}
<Main Class>
아래는 실제 위의 클래스를 사용한 메인 코드이다.
public class StarMain {
public static void main(String[] args) {
Marine m1 = new Marine(40);
System.out.println(m1);
int a = 10;
Marine m2 = new Marine(40);
Tank t1 = new Tank(150);
Dropship d1 = new Dropship(150);
m1.attack(t1);
System.out.println(t1 + "의 HP : " + t1.getHp() + "/" + t1.getMaxHp());
t1.attack(m1);
System.out.println(m1 + "의 HP : " + m1.getHp() + "/" + m1.getMaxHp());
m1.attack(d1);
System.out.println(d1 + "의 HP : " + d1.getHp() + "/" + d1.getMaxHp());
}
}
