Post

추상클래스와 인터페이스

추상클래스(abstract class)란?

하나 이상의 추상 메소드를 포함하는 클래스
  • 추상 클래스는 미완성된 클래스이다.
    • 클래스 앞에 abstract 키워드를 붙인다.
  • 미완성된 메소드인 추상 메소드를 포함하고 있다.
  • 혼자서는 클래스의 역할을 하지 못한다.
  • 새로운 클래스를 작성할 때 부모 클래스의 역할을 할 수 있다.
  • 추상 클래스를 상속받는 모든 서브 클래스들은 추상 메소드를 반드시 재정의(오버라이딩)해야 한다.


추상클래스의 특징

위에서 설명한 개념들의 연장선이다.

  • 일반 메소드도 포함할 수 있지만 추상 메소드를 하나라도 포함하고 있다면 추상 클래스로 선언해야 한다.
  • new 연산자 사용을 통한 인스턴스화가 불가능하다.
  • 어느정도 작성된 상태로 새로운 클래스를 생성하게 되어 개발 단계에서 공통 부분을 줄여주고 개발 시간을 줄여준다.


추상클래스 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
// abstract 키워드 사용
public abstract class Test{
  // 추상 메소드
  // abstract 키워드는 생략 가능하다.
  // 상속받은 서브클래스에서 반드시 재정의 해야한다.
  public abstract void testMethod();

  // 일반 메소드
  // 이미 구현되어 있기 때문에 재정의 할 필요가 없다.
  public void testMethod2(){
    .
    .
    .
  };
}

public class Child extends Test {
  @Override
  void testMethod(){
    System.out.println("child");
  }
}


인터페이스의 등장 이유

  • 자식 클래스가 여러 부모 클래스를 상속받을 수 있다면, 다양한 동작을 수행할 수 있다는 장점을 가지게 된다.
  • 하지만 클래스를 이용하여 다중 상속을 할 경우 메소드 출처의 모호성 등 여러 가지 문제가 발생할 수 있어 자바에서는 클래스를 통한 다중 상속을 지원하지 않는다.
  • 하지만 다중 상속의 이점을 버릴 수 없기에 인터페이스를 통해 다중 상속을 지원한다.


인터페이스(interface)란?

추상 메소드와 상수만을 포함하는 일종의 추상 클래스
  • 인터페이스란 다른 클래스를 작성할 때 기본이 되는 틀을 제공하면서, 다른 클래스 사이의 중간 매개 역할까지 담당하는 일종의 추상 클래스를 의미한다.
  • 추상 클래스는 추상 메소드 뿐만 아니라 생성자, 필드, 일반 메소드도 포함할 수 있다. 하지만 인터페이스는 추상 메소드와 상수만을 포함할 수 있다.
  • 추상 클래스를 부분적으로만 완성된 미완성 설계도라고 한다면 인터페이스는 구현된 것이 아무것도 없는 기본 설계도라 할 수 있다.


인터페이스의 상속

  • 인터페이스는 인터페이스로부터만 상속(extends) 받을 수 있다.
  • 클래스와는 달리 다중 상속, 즉 여러 개의 인터페이스로부터 상속 받는 것이 가능하다.


인터페이스의 구현

  • 추상 클래스와 마찬가지로 자신이 직접 인스턴스를 생성할 수 없다. 따라서 인터페이스가 포함하고 있는 추상 메소드를 구현해 줄 클래스를 작성해야 한다.
  • 상속은 클래스를 확장한다는 의미의 키워드인 extends를 사용하며, 인터페이스는 구현한다는 의미의 키워드인 implements를 사용한다.


인터페이스의 다형성

  • 자손 클래스의 인스턴스를 조상 타입의 참조 변수로 참조하는 것이 가능하다.


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public class TestPolymorphism {
    public static void main(String[] args) {
        Animal myDog = new Dog();
        Animal myCat = new Cat();

        myDog.makeSound(); // 출력: Woof
        myCat.makeSound(); // 출력: Meow
        
        // 배열을 사용하여 다형성을 보여주는 예
        // 각 객체들을 Dog, Cat 이 아닌 Animal 타입의 참조 변수로 다룸으로써 for문을 쉽게 작성할 수 있다.
        Animal[] animals = {new Dog(), new Cat()};
        for (Animal animal : animals) {
            animal.makeSound();
        }
    }
}


인터페이스 장점

  1. 대규모 프로젝트 개발 시 일관되고 정형화된 개발을 위한 표준화가 가능하다.
  2. 클래스의 작성과 인터페이스의 구현을 동시에 진행할 수 있으므로, 개발 시간을 단축할 수 있다.
  3. 클래스와 클래스 간의 관계를 인터페이스로 연결하면 클래스마다 독립적인 프로그래밍이 가능하다.


인터페이스 제약사항

  • 모든 멤버 변수는 public static final 이어야 하며, 생략 가능
  • 모든 메소드는 public abstract 이어야 하며, 생략 가능
  • JDK 1.8부터 인터페이스에 static 메소드와 default 메소드의 추가를 허용했다.

static 메소드default 메소드


인터페이스 예시

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
interface Animal{
  public static final int AGE = 5;
  final int WEIGHT = 12;
  static int HEIGHT = 60;
  int LEG = 4;
  // 위의 상수는 모두 public static final이며, 생략된 형태이다.

  public abstract int getAge();
  int getWeight();
  default int getHeight(){
    return HEIGHT;
  }
  static int getLeg(){
    return LEG;
  }
}
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
interface Vehicle {
    void move();
}

interface Electric {
    void charge();
}

interface Autonomous {
    void navigate();
}

// ElectricVehicle 인터페이스는 Vehicle과 Electric 인터페이스를 모두 상속받는다.
// 따라서 ElectricVehicle 인터페이스를 구현하는 클래스는 move(), charge(), displayBatteryLevel() 메소드를 모두 구현해야 한다.
interface ElectricVehicle extends Vehicle, Electric {
    void displayBatteryLevel();
}

class Car implements Vehicle {
    public void move() {
        System.out.println("Car is moving");
    }
}

// TeslaModelS 클래스는 Car 클래스를 상속받고, ElectricVehicle과 Autonomous 인터페이스를 구현한다.
// Car 클래스는 이미 move()를 구현했으므로, charge(), navigate(), displayBatteryLovel() 만 구현하면 된다.
class TeslaModelS extends Car implements ElectricVehicle, Autonomous {
    public void charge() {
        System.out.println("Tesla Model S is charging");
    }

    public void navigate() {
        System.out.println("Tesla Model S is navigating autonomously");
    }

    public void displayBatteryLevel() {
        System.out.println("Displaying battery level");
    }
}


추상 클래스와 인터페이스의 차이점

  • 추상 클래스
    • 클래스이며, 클래스와 관련이 있다. (주로 베이스 클래스로 사용)
    • 추상 메소드 및 일반 메소드와 멤버도 포함할 수 있다.
    • 한 개의 클래스가 여러 개의 클래스를 상속받을 수 없다. (다중 상속 불가능)
    • 상속을 받아 기능을 확장시키는 데 목적이 있다.
    • 목적
      • 기존의 클래스에서 공통된 부분을 추상화하여 상속하는 클래스에게 구현을 강제화한다.
      • ‘이것은 무엇인가(What it is)’ 에 초점을 맞춘다.
      • 특정 종류의 객체들이 공유하는 기본적인 구조와 기능을 제공하는데 사용한다.


  • 인터페이스
    • 클래스가 아니며, 클래스와 관련이 없다.
    • 추상 메소드와 상수만을 멤버로 가진다.
    • 한 개의 클래스가 여러 인터페이스를 구현할 수 있다. (클래스가 다중 상속은 안되지만 다중 구현은 가능)
    • 목적
      • 구현 객체들의 같은 동작을 보장하기 위해 사용한다.
      • ‘이것을 할 수 있는가(What it can do)’ 에 초점을 맞춘다.
      • 서로 다를 수 있는 객체들이 공통으로 수행할 수 있는 행동을 정의하는 데 사용된다.


클래스를 이용한 다중 상속의 문제점

  • 다중 상속이 된다고 가정했을 때, 아래 코드를 살펴보자
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
class Animal {
  public void cry(){
    System.out.println("짖기!");
  }
}

class Cat extends Animal {
  @Override
  public void cry(){
    System.out.println("냐옹냐옹!");
  }
}

class Dog extends Animal{
  @Override
  public void cry(){
    System.out.println("멍멍!");
  }
}

class MyPet extends Cat, Dog { ... }
// 1.

public class Test {
  public static void main(String[] args){
    MyPet pet = new MyPet();
    pet.cry();
    // 2.
  }
}
  • 다중 상속을 허용할 경우, 발생할 수 있는 문제는 메소드 출처의 모호성이다.
  • 주석을 달아놓은 2에서 MyPet 클래스의 인스턴스인 pet이 cry() 메소드를 호출하면 이 메소드가 Dog 클래스의 cry() 메소드인지, Cat 클래스의 cry() 메소드인지 구분할 수 없는 모호성이 생긴다.
  • 이와 같은 이유로 자바에서 다중 상속을 지원하지 않는다.
  • 하지만, 인터페이스를 이용해 다중 구현을 하게되면 위와 같은 메소드 호출의 모호성을 방지할 수 있다.
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
interface Animal {
  void cry();
}

interface Cat extends Animal {
  void cry();
}

interface Dog extends Animal {
  void cry();
}

class Pet implements Cat, Dog {
  @Override
  public void cry(){
    System.out.println("멍멍~ 냐옹냐옹~")
  }
}

public class Test{
  public static void main(String[] args){
    Pet pet = new Pet();
    pet.cry();
  }
}

// 결과 : 멍멍~ 냐옹냐옹~
  • 구현체인 Pet 에서만 cry()를 정의하므로 메소드 호출의 모호성이 발생하지 않는다.
  • 인터페이스의 제약사항인 일반 메소드 정의 불가 덕분에 다중 상속이 가능한 것이다.


누군가가 물어본다면

추상 클래스는 미완성된 클래스로서 특정 종류의 객체들에게 기본적인 구조와 기능을 제공하는데 사용합니다.
인터페이스는 클래스의 다중 상속 불가에 대한 대안으로 서로 다른 객체들의 공통된 행동을 정의하는데 사용합니다.
This post is licensed under CC BY 4.0 by the author.