인터페이스 (Interface)
/* 육식동물 interface 추가
* interface는 class와 마찬가지로
* 단독파일로 저장하는 것이 일반적인 방법이다.
* */
interface Carnivore{
// 동물 분류에 따른 먹이 구분
String getFood(); // 멤버변수 추가
}
class Animal {
String name;
void setName(String name) {
this.name = name;
}
}
// Tiger, Lion 클래스는 인터페이스를 구현하도록 수정. (implement _name)
class Tiger extends Animal implements Carnivore{
// 인터페이스의 메서드는 항상 public으로 구현해야 함.
public String getFood() {
return "apple";
}
}
class Lion extends Animal implements Carnivore{
public String getFood() {
return "banana";
}
}
// + 추가 가능
class Crocodile extends Animal implements Carnivore{
public String getFood() {
return "watermelon";
}
}
class ZooKeeper{
// void feed(Tiger tiger) { // Method Overloading
// System.out.println("feed apple");
// }
//
// void feed(Lion lion) { // Method Overloading
// System.out.println("feed banana");
// }
// (...)
// 이처럼 동물이 추가될수록 feed 메서드를 매번 추가해야 한다.
// 이에 따라 인터페이스를 추가한다.
// 다음과 같이 수정할 수 있다.
void feed(Carnivore carnivore) {
// System.out.println("feed apple");
// Interface의 추가로 코드 변경
System.out.println("feed " + carnivore.getFood());
}
}
public class TestClass{
public static void main(String []args) {
ZooKeeper zooKeeper = new ZooKeeper();
Tiger tiger = new Tiger();
Lion lion = new Lion();
zooKeeper.feed(tiger); // feed apple
// feed banana → apple(통일)
zooKeeper.feed(lion); // feed banana → apple → banana
// 이와 같이 지정된 육식동물에 따라 제공되는 먹이가 다른 것이
// apple 로 통일된 문제가 있기 때문에
// 인터페이스 내 메서드를 추가해야 한다.
}
}
인터페이스의 핵심과 개념
육식동물의 종류만큼 feed 메서드가 필요했던 ZooKeeper 클래스를 Carnivore 인터페이스를 이용하여
구현하였더니 단 한개의 feed 메서드로 구현이 가능해졌다. 여기서 중요한 점은 메서드의 갯수가 줄어들었다는 점이
아니라 ZooKeeper 클래스가 동물들의 종류에 의존적인 클래스에서 동물들의 종류와 상관없는
독립적인 클래스가 되었다는 점이다. 바로 이 점이 인터페이스의 핵심이다.
이번에는 좀 더 개념적으로 인터페이스를 생각해보자.
USB 포트에 연결할 수 있는 기기는 하드디스크, 메모리스틱, 스마트폰 등 무척 많다.
바로 이 USB 포트가 물리적 세계의 인터페이스라고 할 수 있다.
USB 포트의 규격만 알면 어떤 기기도 만들 수 있다.
또 컴퓨터는 USB 포트만 제공하고 어떤 기기가 만들어지는지는 신경 쓸 필요가 없다.
바로 이 점이 인터페이스와 매우 비슷하다.
Inheritance 와 Interface
위에서 작성한 Carnivore 메서드 대신 Animal 클래스에 getFood 메서드를 추가하고
Tiger, Lion 등에서 getFood 메서드를 오버라이딩한 후 Zookeeper의 feed 메서드가 Carnivore 대신
Animal 을 입력 자료형으로 사용해도 동일한 효과를 만들 수 있다.
하지만 상속은 자식클래스가 부모 클래스의 메서드를 오버라이딩하지 않고 사용할 수 있기 때문에
해당 메서드를 반드시 구현해야 한다는 "강제성"을 갖지 못한다.
상황에 맞게 상속을 사용할 것인지 인터페이스를 사용해야 할지를 결정해야 하겠지만
인터페이스는 인터페이스의 메서드를 반드시 구현해야 한다는 "강제성"을 갖는다.
디폴트 메서드
자바 8버전 이후로 디폴트 메서드(default method)를 사용할 수 있다.
인터페이스의 메서드는 몸통(구현체)를 가질 수 없지만 디폴트 메서드를 사용하면 실제 구현된 형태의 메서드를 가짐.
interface Carnivore{
// 동물 분류에 따른 먹이 구분
String getFood(); // 멤버변수 추가
default void printFeed() {
System.out.printf("my food is %s\n", getFood());
}
}
디폴트메서드는 메서드명 가장 앞에 "default" 라고 표기해야 한다.
이렇게 Carnivore 인터페이스에 printFeed 디폴트 메서드를 구현하면 Carnivore 인터페이스를 구현한
Tiger, Lion 등의 실제 클래스는 printFeed 메서드를 구현하지 않아도 사용할 수 있다.
그리고 디폴트 메서드는 Overriding(덮어쓰기)이 가능하다.
즉, printFeed 메서드를 실제 클래스에서 다르게 구현하여 사용할 수 있다.
스태틱 메서드
자바8 버전 이후로부터는 인터페이스에 스태틱 메서드(static method)를 사용할 수 있다.
인터페이스에 스태틱 메서드를 구현하면 인터페이스명.스태틱메서드명 과 같이 사용하여
일반클래스의 스태틱 메서드를 사용하는 것과 동일하게 사용할 수 있다.
interface Carnivore{
// 동물 분류에 따른 먹이 구분
String getFood(); // 멤버변수 추가
default void printFeed() {
System.out.printf("my food is %s\n", getFood());
}
int LEG_COUNT = 4; // 인터페이스 상수
static int speed() {
return LEG_COUNT * 30;
}
}
이렇게 스태틱 메서드를 추가하면 다음과 같이 사용할 수 있다.
Carnivore.speed();
인터페이스 상수
위 코드에서 사용한 int LEG_COUNT = 4; 문장은 인터페이스에 정의한 상수이다.
인터페이스에 정의한 상수는 int LEG_COUNT = 4; 처럼 public static final 을 생략해도 자동으로
public static final 이 적용된다. (다른 형태의 상수 정의는 불가능하다.)
※ sample code
package package_01;
/* 육식동물 interface 추가
* interface는 class와 마찬가지로
* 단독파일로 저장하는 것이 일반적인 방법이다.
* */
interface Carnivore{
// 동물 분류에 따른 먹이 구분
String getFood(); // 멤버변수 추가
default void printFeed() {
System.out.printf("my food is %s\n", getFood());
}
int LEG_COUNT = 4; // 인터페이스 상수
static int speed() {
return LEG_COUNT * 30;
}
}
class Animal {
String name;
void setName(String name) {
this.name = name;
}
}
// Tiger, Lion 클래스는 인터페이스를 구현하도록 수정. (implement _name)
class Tiger extends Animal implements Carnivore{
// 인터페이스의 메서드는 항상 public으로 구현해야 함.
public String getFood() {
return "apple";
}
}
class Lion extends Animal implements Carnivore{
public String getFood() {
return "banana";
}
}
// + 추가 가능
class Crocodile extends Animal implements Carnivore{
public String getFood() {
return "watermelon";
}
}
class ZooKeeper{
// void feed(Tiger tiger) { // Method Overloading
// System.out.println("feed apple");
// }
//
// void feed(Lion lion) { // Method Overloading
// System.out.println("feed banana");
// }
// (...)
// 이처럼 동물이 추가될수록 feed 메서드를 매번 추가해야 한다.
// 이에 따라 인터페이스를 추가한다.
// 다음과 같이 수정할 수 있다.
void feed(Carnivore carnivore) {
// System.out.println("feed apple");
// Interface의 추가로 코드 변경
System.out.println("feed " + carnivore.getFood());
}
}
public class TestClass{
public static void main(String []args) {
ZooKeeper zooKeeper = new ZooKeeper();
Tiger tiger = new Tiger();
Lion lion = new Lion();
zooKeeper.feed(tiger); // feed apple
// feed banana → apple(통일)
zooKeeper.feed(lion); // feed banana → apple → banana
// 이와 같이 지정된 육식동물에 따라 제공되는 먹이가 다른 것이
// apple 로 통일된 문제가 있기 때문에
// 인터페이스 내 메서드를 추가해야 한다.
}
}
인터페이스(interface)
자식 클래스가 여러 부모 클래스를 상속 받을 수 있다면,
다양한 동작을 수행할 수 있다는 장점을 가지게 될 것이다.
하지만 클래스를 이용하여 다중 상속을 할 경우 메서드 출처의 모호성 등 여러 가지 문제가 발생할 수 있어
자바에서는 클래스를 통한 다중 상속은 지원하지 않는다.
하지만 다중 상속의 이점을 버릴 수는 없기에 자바에서는 인터페이스라는 것을 통해 다중 상속을 지원하고 있다.
인터페이스란 다른 클래스를 작성할 때 기본이 되는 틀을 제공하면서,
다른 클래스 사이의 중간 매개 역할까지 담당하는 일종의 추상 클래스를 의미한다.
자바에서 추상 클래스는 추상 메서드 뿐만 아니라 생성자, 필드, 일반 메서드도 포함할 수 있다.
하지만 인터페이스(interface)는 오로지 추상 메서드와 상수만을 포함할 수 있다.
인터페이스의 선언
자바에서 인터페이스를 선언하는 방법은 클래스를 작성하는 방법과 같다.
인터페이스를 선언할 때에는 접근제어와 함께 interface 키워드를 사용하면 된다.
자바에서 인터페이스는 다음과 같이 선언한다.
접근제어자 interface 인터페이스이름{
public static final 타입 상수 이름 = 값'
...
public abstract 메서드이름(매개변수목록);
...
}
단 클래스와는 달리 인터페이스 모든 필드는 public static final이어야 하며, 모든 메서드는 public abstract 이어야 한다.
이 부분은 모든 인터페이스 공통으로 적용되는 부분이므로 이 제어자는 생략할 수 있다.
이렇게 생략된 제어자는 컴파일 시 자바 컴파일러가 자동으로 추가해준다.
인터페이스의 구현
인터페이스는 추상 클래스와 마찬가지로 자신이 직접 인스턴스를 생성할 수 없다.
따라서 인터페이스가 포함하고 있는 추상 메서드를 구현해 줄 클래스를 작성해야만 한다.
자바에서 인터페이스는 다음과 같은 문법을 통해 구현한다.
class 클래스 이름 implements 인터페이스 이름{
...
}
만약 모든 추상 메서드를 구현하지 않는다면, abstract 키워드를 사용하여 추상 클래스로 선언해야 한다.
다음 예제는 인터페이스를 구현하는 예제이다.
interface Animal{
// public abstract void cry();
void cry();
}
class Cat implements Animal{
public void cry() {
System.out.println("Meow~");
}
}
class Dog implements Animal{
public void cry() {
System.out.println("Woof!");
}
}
public class TestClass{
public static void main(String[] args) {
Cat c = new Cat();
Dog d = new Dog();
c.cry();
d.cry();
}
}
//Meow~
//Woof!
자바에서는 다음과 같이 상속과 구현을 동시에 할 수 있다.
class 클래스이름 extends 상위클래스이름 implements 인터페이스이름{
...
}
인터페이스는 인터페이스로부터만 상속을 받을 수 있으며, 여러 인터페이스를 상속 받을 수 있다.
다음 예제는 인터페이스를 사용한 다중 상속의 예제이다.
interface Animal{
// public abstract void cry();
void cry();
}
interface Pet{
// public abstract void play();
void play();
}
class Cat implements Animal,Pet{
public void cry() {
System.out.println("Meow~");
}
public void play() {
System.out.println("Play a Catching-Mouse!");
}
}
class Dog implements Animal,Pet{
public void cry() {
System.out.println("Woof!");
}
public void play() {
System.out.println("Go for a walk!");
}
}
class TestClass{
public static void main(String[] args) {
Cat c = new Cat();
Dog d = new Dog();
c.cry();
d.cry();
c.play();
d.play();
}
}
//Meow~
//Woof!
//Play a Catching-Mouse!
//Go for a walk!
위의 예제에서 Cat 클래스와 Dog 클래스는 각각 Animal과 Pet 이라는 두 개의 인터페이스를 동시에 구현하고 있다.
'Language > Java' 카테고리의 다른 글
Java - Abstract Class (0) | 2023.03.27 |
---|---|
Java - Polymorphism (0) | 2023.03.27 |
Java - Constructor (0) | 2023.03.26 |
Java - Inheritance (0) | 2023.03.26 |
Java - Call by value (0) | 2023.03.25 |