Post

Java: 추상 클래스, 다형성, 인터페이스

컴퓨터 내에 동물(Animal)이라는 객체를 정의한다고 가정합니다.

동물의 행위는 여러 가지가 있겠지만, 일단 생존에 있어 필수적인 먹는 것과 자는 것을 우선적으로 고려하기로 하겠습니다.

그래서 동물의 정의를 기록하면 클래스(Class)라는 개념이 등장하는데, 여기서 명사인 ‘동물‘을 클래스라 하고 동사인 ‘먹다’, ‘자다’ 이런 것들은 _메소드_라 합니다.

여기서 좀 더 확장해서 배우는 개념들이 상속, 추상화, 다형성 이런 것들인데요 상속은 말 그대로 상위 클래스를 상속받는 것입니다.

동물은 그냥 동물 단일 종이 아니며 개구리, , 곤충, 포유류 등으로 세분화할 수 있으며, 개구리도 황소개구리, 어떤개구리 등등으로 더 세분화할 수 있습니다. 참고로 동물의 사전 정의를 찾아보면 곤충도 동물에 속하므로 어디가서 “곤충이 무슨 동물이냐”는 창피한 소리는 하지 말도록 합시다.

 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
package blog.animal.abst;

public abstract class AbstractAnimal {
  
  public void eat() {
    System.out.println("먹는다.");
  }
  
  public void sleep() {
    System.out.println("잔다");
  }
  
  public abstract void move();

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
package blog.animal.abst;

public class Frog extends AbstractAnimal{
  @Override
  public void move() {
    System.out.println("폴짝폴짝");
  }
  
  // 이미 존재하는 메소드에 대해서도 오버라이딩 할 수 있다.
  @Override
  public void eat() {
    System.out.println("곤충을 먹는다.");
  }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package blog.animal.abst;

public class AnimalTest1 {
  public static void main(String[] args) {
    
    // AbstractAnimal은 원칙적으로 new 키워드를 사용해 객체를 만들 수 없다.
    // 다만 익명 메소드를 이용해 즉석에서 오버라이딩해서 사용할 수 있다.
    AbstractAnimal frog0 = new AbstractAnimal() {			
      @Override
      public void move() {
        System.out.println("풀쩍풀쩍");				
      }
    };
    
    AbstractAnimal frog1 = new Frog();
    frog1.eat();
    frog1.move();
    
    // 익명 메소드를 이용해 생성한 인스턴스도 사용 가능하다.
    frog0.move();
  }
  

}

1
2
3
파충류를 먹는다.
폴짝폴짝
풀쩍풀쩍

 

인터페이스

1
2
3
4
5
6
7
package blog.animal;

public interface LivingThing {
  public LivingThing grow();
  public void die(); 
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package blog.animal;

public interface Animal extends LivingThing {
  
  // 인터페이스는 인터페이스를 상속받을 수 있음(extends 키워드 사용)
  // move()는 이 인터페이스를 구현하는 클래스들이 반드시 오버라이딩 해야함.
  public Animal move();

  /* JDK 8 이후 */

  // default 메소드: abstract class에서 선언된 일반 메소드랑 동일.
  // 이 인터페이스를 구현하는 클래스들은 오버라이딩 할 수 있다.
  // 인터페이스 구현 시 인터페이스 내의 모든 추상 메소드를 전부 오버라이딩해야 하는 문제점이 해결됨
  public default Animal sleep() {
    System.out.println("zzz");
    return this;
  }
  
  public default Animal eat() {
    System.out.println("냠냠");
    return this;
  }

  // static 메소드: Animal.checkHealth() 로 사용
  // 스태틱 방식을 이용해 인터페이스에 바로 접근
  // 인터페이스만으로도 독립된 프로그램이 작동할 수 있음
  public static void checkHealth() {
    System.out.println("동물의 건강 상태를 체크합니다.");
  }
}

1
2
3
4
5
6
7
package blog.animal;

public interface Pet {
  public Pet stroll();
  public Pet cleanMyRoom();
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
package blog.animal;

// 인터페이스는 여러 개를 구현하는 것이 가능하다. 
// 다중 상속과 유사한 효과(엄밀이 말하면 다르긴 하다)를 지닌다.
public class Frog implements Animal, Pet {	
  
  @Override
  public void die() {
    System.out.println("으악");
  }
  
  // 인터페이스에서 public LivingThing grow();로 정의되어 있지만
  // Animal을 사용하는 것이 가능.
  @Override
  public Animal grow() {
    System.out.println("쑥쑥");
    return this;
  }
  
  // eat 메소드는 Animal 인터페이스에서 default 메소드로 정의되어있음
  
  @Override
  public Animal move() {	
    System.out.println("폴짝폴짝.. 약간 점프하긴 하지만 "
        + "대체적으로 땅에 붙어 있다.");
    return this;
  }
  
  // Animal의 디폴트 메소드인 sleep을 오버라이딩한다.
  @Override
  public Animal sleep() {
    System.out.println("개굴개굴 zzz...");
    return this;
  }
  
  @Override
  public Pet stroll() {
    System.out.println("산책나간다");
    return this;
  }
  
  @Override
  public Pet cleanMyRoom() {
    System.out.println("집청소를 한다");
    return this;
  }
}
 

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
package blog.animal;

public class Sparrow implements Animal, Pet{
  @Override
  public Pet cleanMyRoom() {
    System.out.println("집청소를 한다.");
    return this;
  }
  @Override
  public void die() {
    System.out.println("갑자기 유리창에 돌진한다.");			
  }
  @Override
  public LivingThing grow() {
    System.out.println("무럭무럭");
    return this;
    
  }
  @Override
  public Animal move() {
    System.out.println("날개를 이용해 하늘을 난다. 갑작스러운걸 보니"
        + "사람이 근처에 왔나보다");
    return this;
    
  }
  @Override
  public Pet stroll() {
    System.out.println("주인만 떼놓고 공원 주위를 빙빙 날고있다."
        + " 근데 사실 주인이 노진구라 대나무헬리콥터 탈 수 있음");
    return this;
    
  }
  @Override
  public Animal sleep() {
    Animal.super.sleep();
    return this;
  }
  @Override
  public Animal eat() {
    Animal.super.eat();
    return this;
  }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package blog.animal;

public class AnimalTest2 {
  
  public static void main(String[] args) {
    Animal frogger = new Frog();
    Animal suzume = new Sparrow();
    
    // Pet interface는 Animal, LivingThing과 연관이 없기 때문에
    // 타입캐스팅을 해야 한다.
    System.out.println("======== 개구리 ========");
    ((Pet)frogger).stroll().cleanMyRoom();
    frogger.eat().move().sleep().grow().die();
    
    System.out.println();
    
    System.out.println("======== 참새 ========");
    ((Pet)suzume).stroll().cleanMyRoom();
    suzume.eat().move().sleep().grow().die();
    
    System.out.println();
    
    System.out.println("======== static method ========");
    Animal.checkHealth();
    // suzume.checkHealth(); <= 불가
    
  }
  

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
======== 개구리 ========
산책나간다
집청소를 한다
냠냠
폴짝폴짝.. 약간 점프하긴 하지만 대체적으로 땅에 붙어 있다.
개굴개굴 zzz...
쑥쑥
으악

======== 참새 ========
주인만 떼놓고 공원 주위를 빙빙 날고있다. 근데 사실 주인이 노진구라 대나무헬리콥터 탈 수 있음
집청소를 한다.
냠냠
날개를 이용해 하늘을 난다. 갑작스러운걸 보니사람이 근처에 왔나보다
zzz
무럭무럭
갑자기 유리창에 돌진한다.

======== static method ========
동물의 건강 상태를 체크합니다.
This post is licensed under CC BY 4.0 by the author.