안녕하세요, 코린이의 코딩 학습기 채니 입니다.
개인 포스팅용으로 내용에 오류 및 잘못된 정보가 있을 수 있습니다.
Animal 부모 클래스
public abstract class Animal {
public abstract void say();
public abstract void attack();
}
Lion 클래스
public class Lion extends Animal {
@Override
public void say() {
System.out.println("안녕하세요, 라이언입니다.");
}
@Override
public void attack() {
punch();
}
public void punch() {
System.out.println("라이언 펀치!");
}
}
Tiger 클래스
public class Tiger extends Animal {
@Override
public void say() {
System.out.println("안녕하세요, 타이거입니다.");
}
@Override
public void attack() {
kick();
}
public void kick() {
System.out.println("타이거 킥!");
}
}
인터페이스(interface)
- 추상 메소드를 통해 자식 클래스에서의 메소드 구현을 강제화
- 객체화할 수 없기 때문에 자식 클래스를 객체화 하여 사용하거나 익명 이너 클래스 사용
- 모든 메소드는 public abstract로 정의 (생략 시 자동 삽입)
- 모든 필드는 public static final로 정의 (생략 시 자동 삽입)
- 다중 상속 가능!
Barkable 인터페이스 클래스를 만들어보았습니다.
public interface Barkable {
//public static final int MAX_SOUND_LEVEL = 100; 와 같음
int MAX_SOUND_LEVEL = 100;
//public abstract void bark(String sound)와 같음
void bark(String sound);
}
그 후 Tiger, Lion 클래스가 Barkable 클래스를 상속하게 하였습니다. (대표적인 예로 Tiger클래스만)
인터페이스 상속 방법
클래스명 implements 인터페이스명, 인터페이스명 ... {
//내용
}
public class Tiger extends Animal implements Barkable {
@Override
public void say() {
System.out.println("안녕하세요, 타이거입니다.");
}
@Override
public void attack() {
kick();
}
public void kick() {
System.out.println("타이거 킥!");
}
}
Tiger 클래스에 'The type Tiger must implement the inherited abstract method Barkable.bark(String)' 컴파일 오류가 발생하는 것이 확인 되었습니다.
Barkable 클래스의 미완성 메소드 bark()를 반드시 구현 시켜야 한다는 오류 메세지입니다.
@Override
public void bark(String sound) {
System.out.println("타이거가 " + sound + "~ 짖습니다.");
System.out.println(MAX_SOUND_LEVEL + "을 넘겼습니다.");
}
bark() 메소드를 구현 시키니 오류가 사라졌습니다.
//메인 메소드
public void test7() {
Barkable tiger = new Tiger();
tiger.bark("어흥");
}
@콘솔출력값
타이거가 어흥~ 짖습니다.
100을 넘겼습니다.
dog 클래스를 만들어서 Tiger 클래스와 똑같이 만들어보았습니다.
public class Dog extends Animal implements Barkable {
@Override
public void say() {
System.out.println("하이 나는 댕댕이");
}
@Override
public void attack() {
System.out.println("댕댕펀치!");
}
@Override
public void bark(String sound) {
System.out.println("댕댕이가 " + sound + "! 하고 짖습니다.");
System.out.println(MAX_SOUND_LEVEL + "에 미치지 못하였습니다.");
}
}
//메인메소드
public void test7() {
Barkable dog = new Dog();
dog.bark("멍");
}
@콘솔출력값
댕댕이가 멍! 하고 짖습니다.
100에 미치지 못하였습니다.
익명 이너 클래스를 사용해서 구현도 해보겠습니다.
//Eagle 클래스
public class Eagle extends Animal implements Barkable {
@Override
public void say() {
System.out.println("나는 독수리야");
}
@Override
public void attack() {
System.out.println("발톱공격!");
}
@Override
public void bark(String sound) {
}
}
//메인 메소드
public void test8() {
Barkable eagle = new Eagle() {
@Override
public void bark(String sound) {
System.out.println("독수리가 " + sound + "~ 날았습니다.");
}
};
eagle.bark("푸드덕");
}
@콘솔출력값
독수리가 푸드덕~ 날았습니다.
클래스와 인터페이스 간 상속 조합
클래스 extends 클래스 { ~ } | 인터페이스 extends 인터페이스 { ~ } |
클래스 implements 인터페이스 { ~ } |
인터페이스가 인터페이스를 상속할 때는 implements가 아닌 extends 키워드를 사용합니다.
인터페이스의 내부에는 완성된 메소드가 들어갈 수 없기 때문에 구현을 할 이유가 없기 때문이죠.
또한, 인터페이스는 클래스를 상속/구현할 수 없습니다.
클래스 내부에는 이미 완성된 메소드가 들어있기 때문에 인터페이스가 완성된 메소드를 물려받을 수 없기 때문이죠.
인터페이스의 다중 상속 가능 이유
클래스에서 다중 상속을 할 수 없던 이유는 두 부모 클래스에 동일한 이름의 필드/메소드가 존재하면 충돌(모호성)이 발생하기 때문이였습니다.
하지만 인터페이스의 모든 필드는 public static으로 정의, 즉 실제 데이터 값은 static 공간에 각각 인터페이스 공간에 분리 되어 존재하기 때문에 충돌이 일어날 수가 없어 문제가 되지 않습니다.
메소드 또한 모두 미완성 메소드이므로 어차피 자식 클래스 내부에서 완성하여 사용할 것이기 때문에 문제가 되지 않는 것이죠.
인터페이스를 상속할 때 주의할 점
인터페이스의 모든 필드/메소드는 public으로 강제 정의됩니다.
따라서 자식 클래스에서 오버라이딩하여 사용 시 접근 지정자 또한 public으로 정의 해줘야 합니다.
오버라이딩 조건이 접근제한자가 좁아질 수 없기 때문이죠!
자바 1.8부터는 인터페이스 내에서 default/static 메소드를 사용할 수 있게 되었습니다.
☞ default 메소드
- 인터페이스 내 완성된 메소드 삽입
- 자식 클래스에서도 오버라이딩하여 사용 가능
- 자식 클래스에서 부모 인터페이스 내부의 디폴트 메소드도 호출할 수 있음
public interface Barkable {
public default void foo() {
System.out.println("가져다쓰거라 default 메소드!");
}
}
//메인메소드
public void test9() {
Barkable barkMember = new Dog();
//default 메소드
barkMember.foo();
}
@콘솔출력값
가져다쓰거라 default 메소드!
인터페이스 클래스 Barkable에 foo() default 메소드를 정의한 후 메소드를 호출하였습니다.
자식 클래스에서 따로 구현한 것도 아닌데, 상속 받은 그대로 출력하는 것을 확인할 수 있습니다.
※ 만일 하나의 인터페이스를 상속하는 자식 클래스가 1000개라고 가정했을 때, 인터페이스에 메소드 하나를 추가하면 1000개의 자식 클래스에 또 추가한 메소드를 구현해줘야 한다..........(퇴사각)
이러한 문제점을 해결해주기 위해서 추가된 기능!
만일 Dog 클래스에서 default 메소드인 foo()를 오버라이딩 한 후 부모 메소드를 호출하려면 어떻게 해야할까?
부모 인터페이스명.super.디폴트메소드명 으로 호출!!
public class Dog extends Animal implements Barkable {
@Override
public void foo() {
Barkable.super.foo();
System.out.println("가져다썼습니다. 자식 클래스의 foo메소드!");
}
}
@콘솔출력값
가져다쓰거라 default 메소드!
가져다썼습니다. 자식 클래스의 foo메소드!
부모 인터페이스의 foo()메소드 "가져다쓰거라 default 메소드!" 가 먼저 출력된 후 "가져다썼습니다. 자식 클래스의 foo메소드!" 가 출력되는 것을 확인할 수 있습니다.
클래스에서의 super키워드와는 달리 super앞에 인터페이스명이 붙는데 그 이유는 인터페이스는 다중 상속이 가능하기 때문이죠.
따라서 반드시 어떤 부모 인터페이스의 메소드를 가져올 것인지 명시해줘야 합니다.
☞ static 메소드
public interface Barkable {
public static void bar() {
System.out.println("객체 생성 없이 사용할 수 있다 static 메소드!");
}
}
//메인메소드
public void test9() {
//static 메소드
Barkable.bar();
}
@콘솔출력값
객체 생성 없이 사용할 수 있다 static 메소드!
'Java > Java' 카테고리의 다른 글
API) 문자열 값을 수정해주는 StringBuilder, StringBuffer (0) | 2022.03.25 |
---|---|
API) 문자열을 구분해주는 split, StringTokenizer (0) | 2022.03.25 |
abstract 제어자) 추상 메소드, 추상 클래스, 익명 이너 클래스 (0) | 2022.03.24 |
다형성) 다형성의 활용(매개변수 선언부, 리턴 타입), 동적 바인딩, 정적 바인딩 (0) | 2022.03.24 |
다형성) instanceof 키워드(캐스팅 가능 여부 확인) (0) | 2022.03.24 |