● 추상 클래스
- 추상메소드가 있는 클래스
- 필드 위에 미구현된 공간이 존재한다.(추상 메소드 부분이 미구현된 상태)
● 추상 클래스를 상속 받은 클래스
1. 반드시 추상 메소드를 override 해야한다.
2. 자식 클래스도 추상 클래스로 만든다.
● 추상 클래스 사용 목적
- 자식 클래스에서 반드시 재정의되어야 하는 메소드를 추상 메소드로 선언함으로써 강제성을 부여할 수 있다.
● 추상 메소드
- 이름만 정의된 메소드
- 기능은 미구현 상태 → body( { } )가 없다
● 인터페이스(틀)
- 추상 클래스를 고도화시킨 문법
- 추상 메소드만 선언할 수 있다.
- 상속 방법 : implements로 상속을 받는다.
- 한 번 밖에 사용하지 못하는 extends를 아낄 수 있다.
※ 자바에서는 모호성이 발생할 수 있기 때문에 원칙적으로 다중 상속을 허용하지 않는다.
※ 하지만 인터페이스의 등장으로 여러개의 인터페이스를 상속 받을 수 있게 되었고, 사실상 다중 상속을 허용하고 있다.
● 어댑터 클래스
- 인터페이스에서 몇 가지만 override 하고 싶을 때는 adapter 클래스를 활용할 수 있다.
- adapter 클래스는 인터페이스의 추상 메소드를 구체화 시켜 놓은 클래스로서,
adapter 클래스를 상속 받은 클래스는 원하는 메소드만 구체화 해도 객체화가 가능하다.
- 통상적으로 두 개 이상의 메소드가 있는 인터페이스는 짝꿍인 어댑터 클래스가 만들어져있다.
실습(추상 클래스와 추상 메소드)
1. 부모 클래스(Parent.java) 생성
// 추상 클래스
public abstract class Parent {
public void normalFunction() {
System.out.println("일반 메소드");
}
// 추상 메소드
public abstract void abstractFunction();
}
2. 자식 클래스(Child.java) 생성
public class Child extends Parent{
@Override
public void abstractFunction() {
System.out.println("자식에서 오버라이딩된 추상 메소드");
}
}
3. Main.java에서 실행
public class Main {
public static void main(String[] args) {
// Parent p = new Parent(); // 추상 클래스는 객체화가 불가능하다.(미구현된 공간이 남아있기 때문)
Child c = new Child(); // 자식에서 오버라이딩해야 객체화가 가능하다.
c.normalFunction();
c.abstractFunction();
}
}
실습(인터페이스)
1. 인터페이스(Soldier.java) 생성
public interface Soldier {
int num = 10; // 오로지 static final 변수만 넣을 수 있기 때문에 static final 이 생략되었다.
static final int num2 = 10; // final : 변수의 값을 변경하지 못하도록 상수화하는 키워드
// 인터페이스는 오로지 추상 메소드만 선언이 가능하다.
// 오직 추상 메소드만 선언이 가능하기 때문에 abstract는 생략 가능하다.
public void eat();
public abstract void sleep();
public abstract void work();
}
2. 인터페이스를 상속받는 클래스(Corporal.java) 생성
public class Corporal implements Soldier {
@Override
public void eat() {
System.out.println("밥을 많이 잘 먹는다.");
}
@Override
public void sleep() {
System.out.println("잠을 잘 잔다.");
}
@Override
public void work() {
System.out.println("일을 잘한다.");
}
}
3. 인터페이스를 상속받는 클래스(SecondMan.java) 생성
public class SecondMan implements Soldier{
@Override
public void eat() {
System.out.println("밥이 잘 넘어가지 않는다.");
}
@Override
public void sleep() {
System.out.println("잠이 잘 오지 않는다.");
}
@Override
public void work() {
System.out.println("일을 열심히 하기는 하지만 잘 못한다.");
}
}
4. 인터페이스를 상속받는 부모 클래스(SoldierAdapter.java) 생성
public class SoldierAdapter implements Soldier {
@Override
public void eat() {;}
@Override
public void sleep() {}
@Override
public void work() {}
}
5. 자식 클래스(Sergeant.java) 생성
public class Sergeant extends SoldierAdapter {
@Override
public void sleep() {
System.out.println("잠을 너무 많이 잔다.");
}
}
6. Main.java에서 실행
public class Main {
public static void main(String[] args) {
// Soldier s = new Soldier(); // 객체화가 불가능하다.(미구현된 상태 존재)
SecondMan sm = new SecondMan();
Corporal c = new Corporal();
Sergeant s = new Sergeant();
Soldier[] ar = {sm, c, s};
for(int i = 0; i < ar.length; i++) {
if(!(ar[i] instanceof SoldierAdapter)) {
ar[i].eat();
}
}
System.out.println(sm instanceof Soldier);
System.out.println(c instanceof Soldier);
Soldier s1 = sm;
Soldier s2 = c;
if(!(s instanceof SoldierAdapter)) {
s.eat();
}
sm.eat();
sm.work();
sm.sleep();
c.eat();
c.work();
c.sleep();
}
}