일종의 추상 클래스
개발 코드와 객체가 서로 통신하는 접점 역할
객체의 사용 방법을 정의한 타입
→ 따라서, 데이터를 저장하는 인스턴스 or 정적 필드 선언 불가
인터페이스는 구현은 없고, 어떤것을 가지고 있다는 것만 선언하는 것
개발 시간 단축
표준화
관계없는 클래스 간에 관계 맺기
독립적인 프로그래밍
문법
interface 인터페이스이름 {
public static final 타입 상수이름 = 값;
public abstract 메서드이름(매개변수 목록);
}
예시
interface PlayingCard {
public static final int SPACE = 4;
final int DIAMOND = 3; // public static final int DIAMOND = 34; 이 생략됨
static int HEART = 2; // public static final int HEART = 2; 이 생략됨
int CLOVER = 1; // public static final int CLOVER = 1; 이 생략됨
public abstract String getCardNumber();
String getCardKind(); // public abstract String getCardKind(); 이 생략됨
}
class 대신, interface를 사용
class 처럼, public/default 접근 제한자만 사용 가능
모든 멤버변수 : public static final -> 생략 가능 (컴파일러가 자동 추가)
모든 메서드 : public abstract -> 생략 가능 (컴파일러가 자동 추가)
Movable 인터페이스
interface Movable {
void move(int x, int y);
}
Attackable 인터페이스
interface Attackable {
void attack(Unit u);
}
Fightable 인터페이스
interface Fightable extends Movable, Attackable { // move(int x, int y)와 attack(Unit u) 메서드를 멤버로 갖게 됨
}
인터페이스는 인터페이스로부터만 상속받을 수 있음
다중 상속이 가능함
이러한 객체를 구현 객체
구현 객체를 생성하는 클래스를 구현 클래스
인터페이스
public interface RemoteControl {
//상수 필드
public int MAX_VOLUME = 10;
public int MIN_VOLUME = 0;
//추상 메소드
//public abstract 리턴타입 메소드이름 (매개변수, ...);
public void turnOn(); //void 는 리턴값이 필요없음
public void turnOff();
public void setVolume(int volume)();
구현 클래스 1
//public class 구현클래스이름 implements 인터페이스이름
public class Television implements RemoteControl {
//필드
private int volume;
//turnOn() 추상 메소드의 실체 메소드
public void turnOn() {
System.out.println("Tv를 켭니다.");
}
//turnOff() 추상 메소드의 실체 메소드
public void turnOff() {
System.out.println("Tv를 끕니다.");
}
//setVolume() 추상 메소드의 실체 메소드
//인터페이스의 상수를 이용해서 volume 필드값을 제한
public void setVolume(int volume) {
if (volume > RemoteConrole.MAX_VOLUME) {
this.volume = RemoteControl.MAX_VOLUME;
} else if (volume < RemoteControl.MIN_VOLUME) {
this.volume = RemoteControl.MIN_VOLUME;
} else {
this.volume = volume;
}
System.out.println("현재 TV 볼륨: " + this.volume);
}
}
구현 클래스 2
//public class 구현클래스이름 implements 인터페이스이름
public class Audio implements RemoteControl {
//필드
private int volume;
//turnOn() 추상 메소드의 실체 메소드
public void turnOn() {
System.out.println("Audio를 켭니다.");
}
//turnOff() 추상 메소드의 실체 메소드
public void turnOff() {
System.out.println("Audio를 끕니다.");
}
//setVolume() 추상 메소드의 실체 메소드
//인터페이스의 상수를 이용해서 volume 필드값을 제한
public void setVolume(int volume) {
if (volume > RemoteConrole.MAX_VOLUME) {
this.volume = RemoteControl.MAX_VOLUME;
} else if (volume < RemoteControl.MIN_VOLUME) {
this.volume = RemoteControl.MIN_VOLUME;
} else {
this.volume = volume;
}
System.out.println("현재 Audio 볼륨: " + this.volume);
}
}
인터페이스
public interface Tv {
public int MIN_VOLUME = 0;
public int MAX_VOLUME = 100;
public void turnOn();
public void turnOff();
public void changeVolume (int volume);
public void changeChannel (int channel);
}
구현 클래스
public class LedTv implements Tv {
@Override
public void turnOn() {
System.out.println("켜다");
}
@Override
public void turnOff() {
System.out.println("끄다");
}
@Override
public void changeVolume(int volume) {
System.out.println("볼륨 조정");
}
@Override
public void changeChannel(int channel) {
System.out.println("채널 조정");
}
}
테스트
public class LedExam {
public static void main(String[] args) {
Tv tv = new LedTv();
tv.turnOn();
tv.changeChannel(8);
tv.changeVolume(39);
tv.turnOff();
}
}
구현 클래스가 작성되면, new 연산자로 객체 생성이 가능하다.
단,
인터페이스 변수 선언 이후에 구현 객체를 대입해야한다.
public class RemoteControlExample {
public static void main(String[] args) {
//인터페이스 RemoteControl 의 변수 rc 를 선언
RemoteControl rc;
//변수 rc 에 구현 객체 Television, Audio 생성
rc = new Television();
rc = new Audio();
//객체 생성의 잘못된 예시
Television tv = new Television();
}
}
인터페이스 1
public interface RemoteControl {
//상수 필드
public int MAX_VOLUME = 10;
public int MIN_VOLUME = 0;
//추상 메소드
//public abstract 리턴타입 메소드이름 (매개변수, ...);
public void turnOn(); //void 는 리턴값이 필요없음
public void turnOff();
public void setVolume(int volume)();
인터페이스 2
public interface Searchable {
void search (String url);
}
구현 클래스
public class SmartTelevision implements RemoteControl, Searchable {
private int volume;
//turnOn() 추상 메소드의 실체 메소드
public void turnOn() {
System.out.println("Tv를 켭니다.");
}
//turnOff() 추상 메소드의 실체 메소드
public void turnOff() {
System.out.println("Tv를 끕니다.");
}
//setVolume() 추상 메소드의 실체 메소드
//인터페이스의 상수를 이용해서 volume 필드값을 제한
public void setVolume(int volume) {
if (volume > RemoteConrole.MAX_VOLUME) {
this.volume = RemoteControl.MAX_VOLUME;
} else if (volume < RemoteControl.MIN_VOLUME) {
this.volume = RemoteControl.MIN_VOLUME;
} else {
this.volume = volume;
}
System.out.println("현재 TV 볼륨: " + this.volume);
}
//Searchable() 추상 메소드의 실체 메소드
public void search (String url) {
System.out.println(url + " 을 검색합니다.");
}
}
인터페이스
public interface RemoteControl {
//상수 필드
public int MAX_VOLUME = 10;
public int MIN_VOLUME = 0;
//추상 메소드
//public abstract 리턴타입 메소드이름 (매개변수, ...);
public void turnOn(); //void 는 리턴값이 필요없음
public void turnOff();
public void setVolume(int volume)();
구현 클래스 1
//public class 구현클래스이름 implements 인터페이스이름
public class Television implements RemoteControl {
//필드
private int volume;
//turnOn() 추상 메소드의 실체 메소드
public void turnOn() {
System.out.println("Tv를 켭니다.");
}
//turnOff() 추상 메소드의 실체 메소드
public void turnOff() {
System.out.println("Tv를 끕니다.");
}
//setVolume() 추상 메소드의 실체 메소드
//인터페이스의 상수를 이용해서 volume 필드값을 제한
public void setVolume(int volume) {
if (volume > RemoteConrole.MAX_VOLUME) {
this.volume = RemoteControl.MAX_VOLUME;
} else if (volume < RemoteControl.MIN_VOLUME) {
this.volume = RemoteControl.MIN_VOLUME;
} else {
this.volume = volume;
}
System.out.println("현재 TV 볼륨: " + this.volume);
}
}
구현 클래스 2
//public class 구현클래스이름 implements 인터페이스이름
public class Audio implements RemoteControl {
//필드
private int volume;
//turnOn() 추상 메소드의 실체 메소드
public void turnOn() {
System.out.println("Audio를 켭니다.");
}
//turnOff() 추상 메소드의 실체 메소드
public void turnOff() {
System.out.println("Audio를 끕니다.");
}
//setVolume() 추상 메소드의 실체 메소드
//인터페이스의 상수를 이용해서 volume 필드값을 제한
public void setVolume(int volume) {
if (volume > RemoteConrole.MAX_VOLUME) {
this.volume = RemoteControl.MAX_VOLUME;
} else if (volume < RemoteControl.MIN_VOLUME) {
this.volume = RemoteControl.MIN_VOLUME;
} else {
this.volume = volume;
}
System.out.println("현재 Audio 볼륨: " + this.volume);
}
}
개발 코드
public class Myclass {
//1. 필드
RemoteControl rc = new Television();
//2. 생성자
Myclass (RemoteControl rc) {
this.rc = rc;
rc.turnOn();
rc.setVolume(5);
}
//4. 메소드
void methodA() {
3. 로컬 변수
RemoteControl rc = new Audio();
rc.turnOn(); //구현 클래스 2 Audio 의 turnOn() 실행
rc.setVolume(5); //구현 클래스 2 Audio 의 setVolume() 실행
}
//4. 메소드
void methodB (RemoteControl rc) {
rc.turnOn();
rc.setVolume(5);
}
}
실행 코드
public class MyclassExample {
public static void main(String[] args) {
//1. 인터페이스를 필드에서 사용할 경우
MyClass myClass1 = new Myclass();
myClass1.rc.turnOn(); //구현 클래스 1 Television 의 메소드 turnOn() 실행
myClass1.rc.setVolume(5); //구현 클래스 1 Television 의 메소드 setVolume() 실행 --> 값 5를 대입
//2. 인터페이스를 생성자에서 사용할 경우
MyClass myClass2 = new MyClass(new Audio()); //개발 코드의 MyClass 객체 생성한 경우, 구현 클래스 2 Audio 의 turnOn(), setVolume() 실행
//3. 인터페이스를 로컬 변수에서 사용할 경우
MyClass myClass3 = new MyClass();
myClass3.methodA();
//4. 인터페이스를 메소드에서 사용할 경우
MyClass myClass4 = new MyClass();
myClass4.methodB(new Television()); //구현 클래스 1 Television 의 메소드 turnOn(), setVolume() 실행
}
}
다른 클래스 작성에 도움을 주는 목적으로 작성되었다.
추상 메서드를 사용할 수 있다.
인터페이스
interface 인터페이스이름 {
public static final 타입 상수이름 = 값;
public abstract 메서드이름(매개변수 목록);
}
추상 클래스
abstract class 클래스이름 {
...
public abstract void 메서드이름();
}
상속과 구현을 동시에 할 경우
class Firgher extends Unit implements Fightable { public void move(int x, int y) { } public void attack(Unit u) { } }
인터페이스
"기본 설계도"
추상 클래스
"미완성 설계도"
인터페이스
HAS - A "~ 을 할 수 있는"
다중 상속의 가능 여부에 따라 용도를 구분
추상 클래스
IS - A "~ 이다"
다중 상속의 가능 여부에 따라 용도를 구분
인터페이스
모든 클래스가 인터페이스를 사용해서 기본 틀을 구성한다면,
공통으로 필요한 기능들도 모든 클래스에서 오버라이딩하여 재정의 해야하는 번거로움이 있다.
인터페이스 사용 시기
: 상속 관계를 쭉 타고 올라갔을때 다른 조상클래스를 상속하는데 같은 기능이 필요할 경우 인터페이스 사용
(ex. Swimable)
추상 클래스
추상 클래스를 통해, 일반 메서드를 작성하여 공통된 기능을 자식 클래스에서 사용할 수 있도록 구성하는 것은 불가능하다.
Java 는 하나의 클래스만 상속이 가능하기 때문이다.
추상클래스 사용 시기
: 상속 관계를 쭉 타고 올라갔을때 같은 조상클래스를 상속하는데 기능까지 완변히 똑같은 기능이 필요한 경우
(ex. attack, printInfo)
Creature 추상클래스
public abstract class Creature {
private int x;
private int y;
private int age;
public Creature(int x, int y, int age) {
this.age = age;
this.x = x;
this.y = y;
}
// 생명체(Creature)라면 나이를 먹는다. → 공통적인 기능 → 하위 클래스에서 상속할 수 있도록 일반 메서드로 구현
public void age() {
age++;
}
// 생명체(Creature)라면, x좌표상으로 이동 할 수 있다. → 공통적인 기능 → 하위 클래스에서 상속할 수 있도록 일반 메서드로 구현
public void move(int xDistance) {
x += xDistance;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
// 모든 생명체에 필요한 기능이지만, 각각 생명체에 따라 다른 기능으로 구현해야 함 → 추상 메서드로 선언하여 하위 클래스에서 처리
public abstract void attack(); // 공격하는 기능
public abstract void printInfo(); // 정보를 출력하는 기능
// toString() 메서드 : 나중에 출력은 간편하게하기 위해서 오버라이딩하여 사용
@Override
public String toString() {
return "x : " + x + ", y : " + y + ", age : " + age;
}
}
Animal 추상클래스
// 동물(Animal)은 생명체(Creature)이기 때문에, Creature 추상클래스를 상속한다.
public abstract class Animal extends Creature {
public Animal(int x, int y, int age) {
super(x, y, age);
}
// 동물은 공격하는 기능을 가지기 때문에, 추상 메서드 attack()를 오버라이딩
@Override
public void attack() {
System.out.println("몸을 사용하여 공격!!");
}
}
Human 추상클래스
// Talkable 인터페이스를 구현 → 동물 클래스와의 차이점
public abstract class Human extends Creature implements Talkable{
public Human(int x, int y, int age) {
super(x, y, age);
}
// 추상 메서드 attack()
@Override
public void attack() {
System.out.println("도구를 사용!!");
}
// 추상 메서드 talk()
@Override
public void talk() {
System.out.println("사람은 말을 할 수 있다.");
}
}
Talkable 인터페이스
public interface Talkable {
abstract void talk();
}
Pigeon 일반클래스
public class Pigeon extends Animal implements Flyable {
public Pigeon(int x, int y, int age) {
super(x, y, age);
}
// 추상 메서드 fly()
@Override
public void fly(int yDistance) {
setY(getY() + yDistance);
}
// 추상 메서드 flyMove()
@Override
public void flyMove(int xDistance, int yDistance) {
setY(getY() + yDistance);
setX(getX() + xDistance);
}
// Creature 추상 클래스의 추상 메서드 printInfo()를 오버라이딩 (Human, Animal 추상 클래스에서도 아래로 위임했던 것)
@Override
public void printInfo() {
System.out.println("Pigeon -> " + toString());
}
}
동물 클래스를 상속하고 날수 있는 동물이기에 Flyable 인터페이스를 구현해주고 해당 메서드들을 구현 해 주었습니다.
Flyable 인터페이스
public interface Flyable {
void fly(int yDistance);
void flyMove(int xDistance, int yDistance);
}
Turtle 일반클래스
public class Turtle extends Animal implements Swimable{
public Turtle(int x, int y, int age) {
super(x, y, age);
}
@Override
public void swimDown(int yDistance) {
setY(getY() - yDistance);
}
// Creature 추상 클래스의 추상 메서드 printInfo()를 오버라이딩 (Human, Animal 추상 클래스에서도 아래로 위임했던 것)
@Override
public void printInfo() {
System.out.println("Turtle -> " + toString());
}
}
Kevin 일반클래스
public class Kevin extends Human implements Programmer, Swimable{
public Kevin(int x, int y, int age) {
super(x, y, age);
}
@Override
public void coding() {
System.out.println("Hello World!");
}
// 추상 메서드 swimDown() 를 오버라이딩 + 추가적인 조건(기능)으로 재정의("y값이 -10이하로 내려가면 죽을 수도 있다.") → 추상클래스 보다는 인터페이스를 사용
@Override
public void swimDown(int yDistance) {
setY(getY() - yDistance);
if(getY() < -10) {
System.out.println("너무 깊이 들어가면 죽을수도 있어!!");
}
}
// Creature 추상 클래스의 추상 메서드 printInfo()를 오버라이딩 (Human, Animal 추상 클래스에서도 아래로 위임했던 것)
@Override
public void printInfo() {
System.out.println("Kevin -> " + toString());
}
}
Programmer 인터페이스
public interface Programmer {
void coding();
}
Swimable 인터페이스
public interface Swimable {
void swimDown(int yDistance);
}
공통된 기능 사용여부와 관련된 인터페이스
거북이는 동물, 케빈은 사람
→ 두 생명체 모두 수영을 할 수 있지만(공통된 기능), 동물/사람 중 수영을 못하는 동물/사람이 있을 수도 있다.
→ 인터페이스로 따로 선언을 해줘서, 각각 수영을 할 수 있는 클래스에 구현
→ 가독성 ↑ + 유지보수 ↑
Main 실행클래스
public class Main {
public static void main(String[] args) {
Pigeon p = new Pigeon(5,10,14);
p.printInfo();
p.age();
p.move(100);
p.printInfo();
p.fly(5);
p.printInfo();
p.flyMove(10, 20);
p.printInfo();
p.attack();
System.out.println();
Kevin kev = new Kevin(0, 0, 35);
kev.printInfo();
kev.age();
kev.move(10);
kev.printInfo();
kev.attack();
kev.coding();
kev.swimDown(20);
kev.printInfo();
kev.talk();
System.out.println();
Turtle tur = new Turtle(100, -10, 95);
tur.printInfo();
tur.age();
tur.move(-100);
tur.printInfo();
tur.attack();
tur.swimDown(1000);
tur.printInfo();
}
}
실행 결과