홍팍이는 개발자다. 동시에 강사이며, 친구이다. 상황에 따라 다양한 역할을 수행한다.
프로그램에서 또한 마찬가지다. 한 객체가 다양한 역할을 수행할 수 있다. 이를 위한 개념. 인터페이스를 학습해보자.
인터페이스란
인터페이스 정의 및 구현
인터페이스의 장점
Interface
interface >> 인터페이스란, 역할을 부여하는 것이다.
character >> 인터페이스는 추상 메소드로 구성된다. 추상 메소드란, 중괄호 내부가 없는 껍데기 메소드이다.
example >> 스마트폰은 알람, 전화기, 메신저의 역할을 한다.
Definition interface and implement
code structure >>
interface NAME { // 프로토타입 메소드들.. }
example code >> Alarm 인터페이스 만들어보자. 그 기능으로는 비프음 내기와 음악 재생을 추가한다. 메소드에 중괄호가 없음을 주의하자. 이러한 메소드를 추상 메소드라 한다.
interface Alarm { public void beep(); // 추상메소드 public void playMusic(); // 추상메소드 }
declare interface implement >> 위에서 만든 인터페이스를 클래스에 구현하자. 이를 위해 implements 키워드를 사용한다.
// SmartPhone 클래스가 Alarm 역할을 수행하기로 함! class SmartPhone implements Alarm { // 내부 구현 생략.. }
proto type method overriding >> 이제 Alarm 인터페이스의 모든 추상 메소드를 재정의(오버라이딩) 해야 한다. 중괄호 내부가 없기 때문이다.
class SmartPhone implements Alarm { // 프로토타입 메소드를 재정의! public void beep() { System.out.println("삐~ 삐삑, 삐비비빅!"); } // 프로토타입 메소드를 재정의! public void playMusic() { System.out.println("아침 해가 빛나는~ 끝이 없는 바닷가!"); } }
Advantage of interface
clearity for programming design >> 인터페이스를 사용하면 프로그램 설계가 보다 명확해 진다.
up-casting relationship >> 서로 다른 객체들이 같은 인터페이스를 구현한다면, 인터페이스를 타입으로 하여 업캐스팅 할 수 있다.
polymorphism >> 여러 인터페이스를 구현하게 함으로써, 한 객체를 여러 타입으로 해석할 수 있게 된다. 이러한 특징을 다형성이라 한다.
interface >> 인터페이스란, 하나의 역할을 정의하는 것입니다.
make interface >> 예를 들어, 경보음을 위한 Alarm이라는 역할은 아래와 같이 만들 수 있습니다.interface Alarm { // 내용 생략.. }
proto type method >> Alarm 인터페이스는 경보음을 내기위해 어떠한 기능을 해야할까요? 그 대략적인 틀을 프로토타입(추상) 메소드를 통해 정의합니다. 여기서 프로토타입 메소드란, 중괄호 내용이 없는 껍데기 메소드입니다.
interface Alarm { // 비프음을 냈으면 좋겠어? 어떻게? 그건 나중에 정할께! public void beep(); // 음악을 재생했으면 좋겠어! 어떻게? 그건 나중에 정할께! public void playMusic(); }
implements class >> 인터페이스를 통해 역할을 정의했다면, 이를 클래스에게 부여할 수 있습니다. 이러한 클래스를 구현체라고 합니다.
// SmartPhone 클래스는 Alarm 인터페이스를 구현하기로 한다! class SmartPhone implements Alarm { 내용 생략.. }
클래스가 특정 인터페이스를 구현(implements)하기로 했다면, 인터페이스를 구성하는 프로토타입(추상) 메소드를 반드시 오버라이딩(재정의)해야 합니다.
class SmartPhone implements Alarm { // beep음은 이렇게 낼거야! public void beep() { System.out.println("삐빅~ 삐비비비빅!"); } // 음악 재생은 이렇게 할거야! public void playMusic() { System.out.println("동해물과 백두산이~🎶"); } }
CODE
public class SmartPhoneTest { public static void main(String[] args) { // 객체 생성 SmartPhone sp = new SmartPhone("아이폰", "010-0000-0000"); // 전화 걸기 sp.call("010-1234-5678"); // 전화벨이 울림 sp.ring(); } } interface Phone { // targetNumber로 전화를 걸거야! 어떻게? 그건 아직 몰라! public void call(String targetNumber); // 전화벨이 울릴거야! 어떻게? 그건 아직 몰라! public void ring(); } class SmartPhone implements Phone { protected String name; protected String phoneNumber; public SmartPhone(String name, String phoneNumber) { this.name = name; this.phoneNumber = phoneNumber; } /* Phone 인터페이스의 모든 프로토타입 메소드를 구현하시오. */ public void call(String targetNumber){ System.out.printf("%s로 전화를 겁니다!", targetNumber); } public void ring(){ System.out.println("전화벨이 울립니다~"); } }
give interface base structure >> 인터페이스는 특정 역할에 대한 대략적인 틀만 정의합니다. 아래는 Healer 인터페이스의 예입니다.
// 힐러 역할 interface Healer { public void healing(); // 체력 회복 public void shield(); // 보호막 생성 }
typing detail code >> 이제 해당 인터페이스를, 특정 클래스에 부여해보도록 하겠습니다. 이때 사용하는 키워드가 바로 implements 입니다.
// 마법사 클래스에게 Healer 인터페이스를 부여 class Wizard implements Healer { ... }
특정 인터페이스를 부여받은 클래스는, 반드시 프로토타입 메소드를 오버라이딩(재정의) 해주어야만 합니다. 인터페이스는 껍데기 메소드로 이루어져있어, 실행될 수 없기 때문입니다. 따라서 아래와 같이 중괄호 내부를 정의해야 합니다.
class Wizard implements Healer { // 메소드 오버라이딩(재정의) public void healing() { System.out.println("체력을 회복시킵니다!"); } // 메소드 오버라이딩(재정의) public void shield() { System.out.println("보호막을 생성합니다!"); } }
CODE
public class SmartPhoneTest2 { public static void main(String[] args) { // 객체 생성 SmartPhone s = new SmartPhone("아이폰"); // 비프음 내기 s.beep(); // 음악 재생하기 s.playMusic("상어송"); } } interface Alarm { public void beep(); // 비프음 public void playMusic(String title); // 음악 재생 } class SmartPhone implements Alarm { private String name; // 모델명 public SmartPhone(String name) { this.name = name; } /* Alarm 인터페이스의 추상 메소드를 재정의하시오. */ public void beep(){ System.out.println("삐빕! 삐비비빕~!"); } public void playMusic(String title){ System.out.printf("[%s]을 재생합니다..!", title); } }
do lots of role >> 하나의 클래스가 여러 개의 인터페이스를 구현할 수 있습니다.
예를 들어, 스마트폰(SmartPhone) 클래스에 알람(Alarm) 및 전화(Phone) 그리고 메신저(Messenger)의 역할을 동시에 부여하는 경우, implements 키워드 뒤쪽에 인터페이스들을 쉼표로 구분하여 적어주면 됩니다. 인터페이스를 구현하기로 선언하였다면, 반드시 프로토타입 메소드들을 오버라이딩해야 함을 잊지 마세요.class SmartPhone implements Alarm, Phone, Messenger { // 내부 생략.. }
이제 SmartPhone은 알람 및 전화기, 또는 메신저 타입으로 해석(업캐스팅)될 수 있습니다.
// 스마트폰 객체 생성 SmartPhone sp = new SmartPhone(); // 스마트폰을 다양하게 해석(업캐스팅 - 상위 타입으로 해석) Alarm a = sp; // 스마트폰은 알람이다. (O) Phone p = sp; // 스마트폰은 전화기이다. (O) Messenger m = sp; // 스마트폰은 메신저이다 (O)
polymorphism >> 이렇게 하나의 객체가 다양한 타입으로 해석되는 것을 다형성(Polymorphism)이라 합니다.
CODEpublic class Polymorphism { public static void main(String[] args) { // 성기사 객체 생성 HolyKnight uther = new HolyKnight("우서", 180); // 탱커로서의 역할 수행 Tanker t = uther; t.increaseHp(); // 힐러로서의 역할 수행 Healer h = uther; h.heal(); } } interface Tanker { public void increaseHp(); } interface Healer { public void heal(); } /* 1. 탱커와 힐러 역할을 부여하세요. */ class HolyKnight implements Tanker, Healer{ private String name; private int hp; public HolyKnight(String name, int hp) { this.name = name; this.hp = hp; } /* 2. Tanker의 메소드를 오버라이딩하세요. */ public void increaseHp(){ System.out.printf("전체 체력 +50 증가시킵니다." ); } /* 3. Healer의 메소드를 오버라이딩하세요. */ public void heal(){ System.out.printf("체력을 +30 회복합니다."); } }
do lots of role >> 하나의 클래스가 여러 개의 인터페이스(역할)를 구현할 수 있습니다. 이는 마치, 홍팍이가 아들/강사/개발자의 역할을 수행하는 것과 같습니다.
interface AAA { public void aaa(); } interface BBB { public void bbb(); } interface CCC { public void ccc(); } // Foo 클래스는 총 3가지 역할(AAA, BBB, CCC)을 수행! class Foo implements AAA, BBB, CCC { ... }
interpret as interface type >> 위 코드에서 Foo 클래스의 객체는 각 인터페이스 타입으로도 해석 가능합니다. 홍팍이가 집에서는 아들, 학원에서는 강사, 회사에서는 개발자인 것처럼 말입니다.
// 객체 생성 Foo obj = new Foo(); // 인터페이스 타입으로 업캐스팅(해석) AAA a = obj; BBB b = obj; CCC c = obj;
인터페이스 타입으로 해석된 객체는, 해당 인터페이스의 메소드만 수행 가능합니다.
a.aaa(); // O b.bbb(); // O c.ccc(); // O a.bbb(); // X b.aaa(); // X c.bbb(); // X
CODE
public class VariousRoles { public static void main(String[] args) { // HongPark 객체 생성 HongPark park = new HongPark(); // 각 직업으로 변신 Developer devPark = park; Instructor instPark = park; Masseur masPark = park; /* 3. 직업별 메소드를 호출하여 코드를 완성하세요. */ devPark.programming("Java"); instPark.teach("자료구조"); masPark.massage(); } } interface Developer { public void programming(String lang); } interface Instructor { public void teach(String subject); } interface Masseur { public void massage(); } /* 1. 홍팍 클래스에 개발자, 강사, 마사지사 역할을 부여하세요. */ class HongPark implements Developer, Instructor, Masseur{ private String name = "홍팍"; /* 2. 구현하기로 한 인터페이스의 메소드를 오버라이딩 하세요.*/ public void programming(String lang){ System.out.printf("%s -> %s 프로그래밍 중!", this.name, lang); } public void teach(String subject){ System.out.printf("%s -> %s 수업 중!", this.name,subject); } public void massage(){ System.out.printf("%s -> 마사지 중!",this.name); } }
interpret as parent type?? >> 업캐스팅(up-casting)이란, 자식 객체를 부모의 타입으로 해석하는 것을 말합니다.
이와 마찬가지로, 인터페이스를 구현하는 객체는 인터페이스 타입으로 업캐스팅 될 수 있습니다.
groupping >> 전혀 다른 객체들일지라도 같은 인터페이스를 구현하였다면, 업캐스팅을 통하여 그룹화가 가능합니다.// 다양한 객체 생성 Bird bird = new Bird(); Helicopter copter = new Helicopter(); Rocket rocket = new Rocket(); // 인터페이스 타입으로 그룹화 Flyable[] flyableThings = { bird, copter, rocket };
CODE
public class ObjectGrouping { public static void main(String[] args) { // 다양한 객체 생성 Bird bird = new Bird(); Helicopter copter = new Helicopter(); Rocket rocket = new Rocket(); // 인터페이스 타입으로 그룹화 Flyable[] flyableThings = { bird, copter, rocket }; // 모든 날것들을 날림 for (int i = 0; i < flyableThings.length; i++) { Flyable temp = flyableThings[i]; temp.fly(); } } } interface Flyable { public void fly(); } class Bird implements Flyable { /* 1. fly() 메소드를 오버라이딩 하세요. */ public void fly(){ System.out.println("<새>가 날개를 퍼덕이며 날아갑니다!"); } } class Helicopter implements Flyable { /* 2. fly() 메소드를 오버라이딩 하세요. */ public void fly(){ System.out.println("<헬기>가 프로펠러를 힘차게 돌리며 날아갑니다!"); } } class Rocket implements Flyable { /* 3. fly() 메소드를 오버라이딩 하세요. */ public void fly(){ System.out.println("<로켓>이 제트 엔진을 분출하며 날아갑니다!"); } }
interface as a type >> 우리는 앞서 인터페이스 타입으로 업캐스팅이 가능함을 배웠습니다.
따라서 다양한 하위 객체들을 상위 인터페이스 타입으로 ArrayList에 저장 가능합니다.// Flyable 타입을 저장할 ArrayList 생성 ArrayList<Flyable> list = new ArrayList<Flyable>(); // 인터페이스를 통한 다양한 하위 객체들을 저장 Flyable b = new Bird(); Flyable h = new Helicopter(); Flyable r = new Rocket(); list.add(b); list.add(h); list.add(r);
CODE
import java.util.ArrayList; public class InterfaceType { public static void main(String[] args) { // 객체 생성 ArrayList<Orderable> list = new ArrayList<Orderable>(); Orderable j = new Food("족발", 19800); Orderable e = new Electronics("에어팟", 199000); Orderable c = new Clothing("셔츠", 49900); list.add(j); list.add(e); list.add(c); // 총합 계산 int sum = 0; for (int i = 0; i < list.size(); i++){ Orderable o = list.get(i); sum += o.discountedPrice(); } // 결과 출력 System.out.printf("총합: %d원", sum); } } interface Orderable { public int discountedPrice(); } class Food implements Orderable { private String name; private int price; public Food(String name, int price) { this.name = name; this.price = price; } /* 1. 오버라이딩을 통해, 음식 할인율을 적용하세요. */ public int discountedPrice(){ return (int)(this.price*0.9); } } class Electronics implements Orderable { private String name; private int price; public Electronics(String name, int price) { this.name = name; this.price = price; } /* 2. 오버라이딩을 통해, 전자기기 할인율을 적용하세요. */ public int discountedPrice(){ return (int)(this.price*0.8); } } class Clothing implements Orderable { private String name; private int price; public Clothing(String name, int price) { this.name = name; this.price = price; } /* 3. 오버라이딩을 통해, 의류 할인율을 적용하세요. */ public int discountedPrice(){ return (int)(this.price*0.7); } }
CODE
import java.util.ArrayList; public class InterfaceReview { public static void main(String[] args) { // 객체 생성 ArrayList<Sounding> list = new ArrayList<Sounding>(); Sounding d = new Dog(); Sounding b = new Baby(); Sounding t = new Tiger(); Sounding r = new Robot(); // 인터페이스 배열 생성 list.add(d); list.add(b); list.add(t); list.add(r); // 소리내기 for (int i = 0; i < list.size(); i++){ Sounding o = list.get(i); o.sound(); } } } /* 인터페이스 및 클래스를 작성하시오. */ interface Sounding{ public void sound(); } class Dog implements Sounding{ public void sound(){ System.out.println("Dog: 멍멍!"); } } class Baby implements Sounding{ public void sound(){ System.out.println("Baby: 응애!"); } } class Tiger implements Sounding{ public void sound(){ System.out.println("Tiger: 어흥!"); } } class Robot implements Sounding{ public void sound(){ System.out.println("Robot: 삐빕!"); } }