[JAVA] 추상클래스(abstract class)와 인터페이스(interface)

Sia Hwang·2022년 11월 14일
0

JAVA

목록 보기
3/6

What is the abstract class?


[이미지 출처] https://learn-tech-tips.blogspot.com/2016/07/object-oriented-programming-abstract-class-vs-interface.html

  • 처음에 객체지향프로그래밍을 공부할 때 어려웠던 것 중 하나가 추상클래스였다. 보통 추상 하면 서양미술에서의 추상화가 생각나고 뭔가 쉽게 알아보기 힘든 이미지였기 때문이다.

  • 하지만 프로그래밍을 공부하며, 프로그래밍에서의 추상화는 복잡한 것을 덜어내고 최대한 공통되는 핵심만 남기는 것이라고 생각하게 되었다.

    추상화 : 클래스간의 공통점을 찾아내서 공통의 조상을 만드는 작업
    구체화 : 상속을 통해 클래스를 구현, 확장하는 작업

  • 그런 의미에서 추상클래스는 클래스를 구성하는 요소 중 공통적으로 딱 필요한 핵심만 들어있는 클래스라고 할 수 있다. 핵심만 들어있다는 것은 해당 클래스를 구성하는 데 꼭 필요한 멤버 변수와 메서드만 들어있다는 말이라고 볼 수 있다.

  • 우리가 핵심 요약을 할 때 길어지는 설명은 덜어내고 요약을 한다. 그런 것처럼 추상클래스에도 메서드의 세세한 구현부는 들어 있지 않다. 즉 메서드의 원형만 있고 내용물은 없는 것이다.

  • 그렇기 때문에 추상클래스로 객체를 생성할 수는 없다. 만약 생성이 가능하다고 해도 메서드의 내용물이 없기 때문에 의미가 없다. 그래서 메서드의 자세한 동작은 추상클래스를 상속받는 자식 클래스에서 해야 한다.

    추상클래스는 새로운 클래스를 작성하는데 있어서 바탕이 되는 조상클래스로서 중요한 의미를 갖는다.

  • 새로운 클래스를 작성할 때 아무것도 없는 상태보다는 완전하지 못하더라도 어느 정도 틀을 갖춘 상태에서 시작하는 것이 나을 것이다. 이 틀을 이용하면 비슷한 기능을 가진 클래스들을 만들 때 같은 멤버 변수와 메서드를 사용하기 때문에 코드에 어느 정도 통일성을 줄 수 있다.

How to implement abstract class?

abstract class A {}
  • 클래스명 앞에 abstract 키워드를 붙이기만 하면 해당 클래스를 추상클래스로 만들 수 있다.
  • 추상클래스에도 생성자가 있으며, 멤버변수와 메서드도 가질 수 있다.

abstract method

  • 선언부만 작성하고 구현부는 없는(틀만 있고 내용물이 없는) 메서드이다. 설계만 해 놓고 실구현은 상속받는 클래스에서 하도록 만들어 놓은 것이다.
  • 이렇게 만들어 놓는 이유는 메서드의 내용이 상속받는 클래스에 따라 달라질 수 있기 때문이다. 그래서 조상 클래스에서는 선언부만을 작성하고, 주석을 덧붙여 어떤 기능을 수행할 목적으로 작성되었는지 알려 주고, 실제 내용은 상속받는 클래스에서 구현하도록 비워 둔 것이다.
/* 주석을 통해 어떤 기능을 수행할 목적으로 작성하였는지 설명한다. */
absctrac 리턴타입 메서드이름();
  • 위와 같은 형태로 만들 수 있다. 구현부를 작성하지 않기 때문에 메서드이름까지만 작성하고 ;을 붙여 문장을 끝낸다.

  • 추상클래스를 상속받는 자식클래스는 오버라이딩을 통해 추상메서드를 모두 구현해 주어야 한다. 만약 하나라도 구현하지 않으면 그 자식 클래스 역시 추상클래스로 지정해 주어야 한다.

Advantage?

  • 굳이 이렇게 써서 얻는 이점이 뭐가 있을까?
  • 메서드를 사용하는 쪽에서는 메서드가 실제로 어떻게 구현되어 있는지 몰라도 메서드의 이름과 매개변수, 리턴타입, 즉 선언부만 알고 있으면 되므로 내용이 없을 지라도 추상메서드를 사용하는 코드를 작성하는 것이 가능하며, 실제로는 자식클래스에 구현된 완성된 메서드가 호출되도록 할 수 있다.
  • 메서드를 사용하고자 하는 쪽에서는 이 클래스가 특정 추상클래스를 구현했다는 것만 알고 있으면 그 안의 메서드들도 모두 호출할 수 있는 것이다.

What is the interface?

  • 인터페이스도 일종의 추상클래스라 할 수 있는데, 추상클래스보다 추상화 정도가 높아서 추상메서드와 상수만 멤버로 가질 수 있다. 멤버변수는 가질 수 없다.
  • 인터페이스의 구현 갯수에는 제한이 없기 때문에 여러 개를 구현하면 다중상속과 같은 효과를 낼 수 있지만 실제 사용 사례를 보면 이렇게 구현하는 경우는 잘 없다.

How to implement interface?

interface 인터페이스 이름 {
  // public static final 생략 가능
  public static final 타입 상수이름 =; 
  // public abstract 생략 가능
  public abstract 리턴타입 메서드이름(매개변수목록);

Restrictions

  • 모든 멤버변수는 public static final 이어야 하며, 이를 생략할 수 있다.
  • 모든 메서드는 public abstract 이어야 하며, 이를 생략할 수 있다. 단, static 메서드디폴트 메서드는 예외(JDK 1.8부터)
  • 생략된 제어자들은 컴파일시에 컴파일러가 자동으로 추가해주기 때문에 생략이 가능하다.

Inheritance of interface

  • 인터페이스는 인터페이스로부터만 상속받을 수 있으며, 클래스와는 달리 다중상속이 가능하다.
  • 인터페이스는 클래스와 달리 최고 조상이 없다.
interface Moveable {
  void move(int x, int y); // 지정된 위치(x, y)로 이동하는 메서드
}

interface Attackable {
  void attack(Unit u); // 지정된 대상(Unit)을 공격하는 메서드
}

interface Fightable extends Movable, Attackable {}
  • 이제 Fightable 인터페이스는 MovableAttackable 인터페이스의 메서드들을 모두 멤버로 갖게 된다.

Implementation of interface

  • 인터페이스는 클래스에도 상속할 수 있다. 이 때 구현한다는 뜻의 implements 키워드를 사용한다.
class 클래스이름 implements 인터페이스이름 {
  // 인터페이스에 정의된 추상메서드를 구현해야 함
}
  • 만약 구현하는 인터페이스 중 일부 메서드만 구현한다면 abstract를 붙여서 추상클래스로 선언해야 한다.
class Fighter extends Unit implements Fightable {}
  • 위와 같이 상속과 구현을 동시에 할 수도 있다.

인터페이스의 이름에는 주로 '~을 할 수 있는'의 의미인 'able'이 많이 쓰이는데, 그 이유는 어떠한 기능 또는 행위를 하는데 필요한 메서드를 제공한다는 의미를 강조하기 위해서이다. 또한 그 인터페이스를 구현한 클래스는 '~를 할 수 있는' 능력을 갖추었다는 의미이기도 하다. 이름이 'able'로 끝나는 것은 인터페이스라고 추측할 수 있지만 모든 인터페이스의 이름이 반드시 'able'로 끝나는 것은 아니다.

Polymorphism with interface

  • 인터페이스 역시 이를 구현한 클래스의 조상이라 할 수 있으므로 해당 인터페이스 타입의 참조변수로 이를 구현한 클래스의 인스턴스를 참조할 수 있으며, 인터페이스 타입으로의 형변환도 가능하다.
Fightable f = (Fightable) new Fighter();
	or
Fightable f = new Fighter();
  • 위와 같은 형태가 가능한 것이다. 자바 개발을 하다 보면 이런 형태의 코드를 많이 보았을 것이다.
  • 인터페이스 타입의 참조변수로는 인터페이스에 정의된 멤버들만 호출이 가능하다.
void attack(Fightable f) {}
  • 따라서 인터페이스는 메서드의 매개변수 타입으로도 사용될 수 있다. 이 메서드를 호출할 때엔 해당 인터페이스를 구현한 클래스의 인스턴스를 매개변수로 넘겨줘야 한다.
Fightable method() {
  return new Fighter();
}
  • 메서드의 리턴타입으로 인터페이스의 타입을 지정하는 것도 가능하다.

Advantages

  1. 개발시간을 단축시킬 수 있다.
    • 일단 인터페이스가 작성되면, 이를 사용해서 프로그램을 작성하는 것이 가능하다. 메서드를 호출하는 쪽에서는 메서드의 내용과 관계없이 선언부만 알면 되기 때문이다.
    • 그리고 동시에 다른 한 쪽에서는 인터페이스를 구현하는 클래스를 작성하게 하면 인터페이스를 구현하는 클래스가 작성될 때까지 기다리지 않고도 양쪽에서 동시에 개발을 진행할 수 있다.
  2. 표준화가 가능하다.
    • 프로젝트에 사용되는 기본 틀을 인터페이스로 작성한 다음, 개발자들에게 인터페이스를 구현하여 프로그램을 작성하도록 함으로써 보다 일관되고 정형화된 프로그램의 개발이 가능하다.
  3. 서로 관계없는 클래스들에게 관계를 맺어 줄 수 있다.
    • 서로 상속관계에 있지도 않고, 같은 조상클래스를 가지고 있지도 않은 서로 아무런 관계도 없는 클래스들에게 하나의 인터페이스를 공통적으로 구현하도록 함으로써 관계를 맺어 줄 수 있다.
  4. 독립적인 프로그래밍이 가능하다.
    • 인터페이스를 이용하면 클래스의 선언과 구현을 분리시킬 수 있기 때문에 실제구현에 독립적인 프로그램을 작성하는 것이 가능하다. 클래스와 클래스간의 직접적인 관계를 인터페이스를 이용해서 간접적인 관계로 변경하면, 한 클래스의 변경이 관련된 다른 클래스에 영향을 미치지 않는 독립적인 프로그래밍이 가능하다.

default method

  • 조상클래스에 새로운 메서드를 추가하는 것은 별 일이 아니지만, 인터페이스에 새로운 메서드를 추가하는 것은 별 일이다. 인터페이스의 추상메서드는 모두 구현해야 하기 때문에 인터페이스가 변경되면 이를 구현한 클래스들도 모두 수정되어야 하기 때문이다.
  • 그래서 인터페이스가 변경되지 않는 것이 가장 좋지만 아무리 설계를 잘해도 언젠가는 변경되기 마련이기 때문에 디폴트 메서드(default method)가 추가되었다.
  • 디폴트 메서드(default method)는 추상메서드의 기본적인 구현을 제공하는 메서드로 추상메서드가 아니기 때문에 디폴트 메서드가 새로 추가되어도 해당 인터페이스를 구현한 클래스를 변경하지 않아도 된다.
interface MyInterface {
  void method();
  default void newMethod() {}
}
  • 이렇게 쓰면 해당 인터페이스를 구현한 클래스들을 변경하지 않아도 된다.

  • 추상메서드의 기본 구현을 제공하기 때문에 중괄호 {}를 꼭 써줘야 한다.

  • 단, 새로 추가된 디폴트 메서드가 기존의 메서드와 이름이 중복되어 충돌하는 경우가 발생하는데, 이를 해결하는 규칙은 다음과 같다.

    1. 여러 인터페이스의 디폴트 메서드 간의 충돌
      • 인터페이스를 구현한 클래스에서 디폴트 메서드를 오버라이딩해야 한다.
    2. 디폴트 메서드와 조상 클래스의 메서드 간의 충돌
      • 조상 클래스의 메서드가 상속되고, 디폴트 메서드는 무시된다.

참고

profile
당면한 문제는 끝까지 해결하기 위해 노력하는 주니어 개발자입니다.

0개의 댓글