추상클래스와 인터페이스
추상 메소드(Abstract Method)
public abstract void abstractMethod();
abstract
키워드와 함께 원형만 선언되고, 코드는 작성되지 않는 메서드abstract
와final
키워드를 동시에 표기할 수 없음
추상 클래스
abstract class 클래스이름 { // 추상 클래스
void 메서드1() {...}
abstract public void 메서드2(); // 추상 메서드
}
- 개념
- 추상 메서드를 최소 한 개 이상 가지고
abstract
로 선언된 클래스- 최소 한 개 이상의 추상 메서드를 포함하는 경우, 반드시 추상 클래스로 선언해야 함
- 추상 메서드가 없어도
abstract
로 선언한 클래스- 추상 메서드가 하나도 없는 경우라도 추상 클래스로 선언할 수 있음
- 추상 메서드를 최소 한 개 이상 가지고
- 구현
- 서브 클래스에서 슈퍼 클래스의 모든 추상 메서드를 오버라이딩하여 실행 가능한 코드로 구현
- 목적
- 객체(인스턴스)를 생성하기 위함이 아니며, 상속을 위한 부모 클래스로 활용하기 위한 것
- 여러 클래스들의 공통된 부분을 추상화하여 상속 받는 클래스에게 구현을 강제화하기 위한 것
- 메서드의 동작을 구현하는 자식 클래스로 책임을 위임
- 추상 클래스의 추상 메서드를 자식 클래스가 구체화하여 그 기능을 확장하기 위함
- 특징
- 추상 메소드뿐만 아니라 생성자, 필드, 일반 메소드도 포함할 수 있음
- 선언만 하기 때문에 인스턴스로 생성될 수 없음
인터페이스(interface)
interface 인터페이스이름 {
public static final String name = "defaultName";
public abstract void setName(String name);
default void print() { System.out.println("Hello World"); }
}
- 개념
- 추상 메소드와 상수만을 포함하며,
interface
키워드를 사용하여 선언
- 추상 메소드와 상수만을 포함하며,
- 구현
- 인터페이스를 상속받고, 추상 메서드를 모두 구현한 클래스를 구현
implements
키워드 사용
- 목적
- 상속받을 서브 클래스에게 구현할 메서드들의 원형을 모두 알려주어, 클래스가 자신의 목적에 맞게 메서드를 구현하도록 하는 것
- 구현 객체의 같은 동작을 보장하기 위한 목적
- 서로 관련이 없는 클래스에서 공통적으로 사용하는 방식이 필요하지만, 기능을 각각 구현할 필요가 있는 경우에 사용
- 특징
- 인터페이스는 상수 필드와 추상 메서드만으로 구성됨
.java
형태의 소스 파일로 작성되고.class
형태로 컴파일 되기에 물리적 형태는 클래스와 동일함- 모든 메서드는 추상 메서드로서,
public abstract
속성이며 생략 가능함 - 상수는
public static final
속성이며, 생략 가능 - 클래스에는 다중 구현을 지원하고, 인터페이스끼리는 다중 상속을 지원함
- 자바 8부터는
default
와static
키워드를 이용하여 일반 메소드처럼 코드를 작성할 수 있음(구현 클래스에 강제성 x)
추상 클래스와 인터페이스
추상 클래스 | 인터페이스 | |
---|---|---|
사용 키워드 | abstract | interface |
사용 가능 변수 | 제한 없음 | static final(상수) |
사용 가능 접근 제어자 | 제한 없음 | public |
상속 키워드 | extends | implements |
다중 상속 여부 | 불가능 | 가능 |
- 공통점
- 인스턴스(객체)는 생성할 수 없음(new 생성자 사용 x, 구현체 사용해야 함)
- 선언만 되어있고 구현 내용이 없음
- 인터페이스와 추상 클래스의 구현 클래스는 추상 메서드를 반드시 구현해야 함(강제)
- 차이점
- 목적 차이
- 추상 클래스는 추상 메서드를 자식 클래스가 구체화하여 그 기능을 확장하기 위해 사용
- 인터페이스는 구현 객체가 같은 동작을 한다는 것을 보장하기 위해 사용
- 추상 클래스는 클래스이지만, 인터페이스는 클래스가 아님
- 추상 클래스는 단일 상속이지만, 인터페이스는 다중 상속 가능
- 추상 클래스는
is a kind of
, 인터페이스는can do this
- 추상 클래스: Appliances(Abstract Class) - TV, Refrigerator
- 인터페이스: Flyable(Interface) - Plane, Bird
- 목적 차이
다중 상속과 단일 상속 (클래스는 왜 단일 상속만 지원하나요?)
- 클래스는 단일 상속만 지원하고, 인터페이스는 다중 상속을 지원한다.
- 만약 클래스에서 다중 상속이 가능하다면
메서드 출처의 모호성
등 여러 문제가 발생할 수 있다. - 클래스 A와 클래스 B에 한 메서드가 동일한 시그니처를 가질 경우, 자식 클래스는 둘 중 어느 것을 상속받아야 할지 파악할 수 없게 되는 문제가 생긴다.
- 관계가 다이아몬드 형태라 하여 다이아몬드 문제라고 한다.
- 인터페이스는 실질적인 구현이 이루어져 있지 않고 추상 메서드만 가지고 있기 때문에, 메서드가 겹치더라도 최종 구현 부분은 구현 클래스에서 새롭게 정의해야 하기 때문에 문제가 없다.
클래스의 경우
public class GranFar {
void say() {
System.out.println("GranFar");
}
}
class FarA extends GranFar {
@Override
void say() {
System.out.println("FarA");
}
}
class FarB extends GranFar {
@Override
void say() {
System.out.println("FarB");
}
}
class Son extends FarA, FarB{ // 컴파일 에러
@Override
void say() {
super.say(); // FarA와 FarB 모두 say 메서드가 있기에 어느 클래스의 메서드 호출인지 모름
}
}
인터페이스의 경우
public interface GranFar {
void say();
}
interface FarA extends GranFar {
@Override
void say();
}
interface FarB extends GranFar {
@Override
void say();
}
interface Son extends FarA, FarB { // 컴파일 에러가 발생하지 않음
@Override
void say(); // 상위 인터페이스에서 구현된 것이 없기에 에러가 발생하지 않는다.
}
- 자바 8부터 등장한 default 메서드를 사용한다면 인터페이스에서도 기능을 구현할 수 있다.
- 하지만 default 메서드를 사용한다면 클래스처럼 다중 상속 문제가 발생하고, 이를 재정의를 통해 해결해주어야 한다.
public interface GranFar {
default void say() {
System.out.println("GranFar");
}
}
interface FarA extends GranFar {
@Override
default void say() {
System.out.println("FarA");
}
}
interface FarB extends GranFar {
@Override
default void say() {
System.out.println("FarB");
}
}
interface Son extends FarA, FarB { // 컴파일 에러
}
- 위 상태에선 say 메서드 실행시 FarA, FarB 둘 중 어느 인터페이스의 default 메서드를 따라야 할지 모르기 때문에 컴파일 에러가 발생한다.
- 때문에 어떤 인터페이스의 메서드를 호출해야하는지 명시해줘야 한다.
interface Son extends FarA, FarB { // 명시해줬기에 컴파일 에러 발생하지 않음
@Override
default void say() {
FarA.super.say();
}
}
동일한 시그니처: 메서드의 접근 제어자, 리턴 타입, 메서드 명, 매개변수가 모두 동일함(구현부는 다를 수 있음)