티스토리 뷰

Language/Java

추상클래스와 인터페이스

DUCKBAE's 2024. 10. 22. 09:21

추상 클래스와 인터페이스는 클래스의 기본 구조를 정의하는 공통점이 있지만 차이점이 있다.

본 글에서는 추상 클래스와 인터페이스의 차이를 알아보려고한다.


 

추상클래스와 목적

추상 클래스는 클래스들의 공통적인 필드와 메서드를 추출해서 선언한 클래스로, 실체 클래스와 상속의 관계를 가진다.

공통된 속성과 행동을 통일할 목적으로 사용하는데, 실체 클래스가 여러개이고 설계하는 사람도 여러명이라고 가정했을 때 실체 클래스마다 속성과 행동이 다를 수 있다.

예를 들어 전기차, 하이브리드차, 내연기관차는 자동차라는 공통점이 있다. 바퀴, 창문, 라이트와 같이 공통된 속성이 있고 전진과 후진이라는 행동을 가진다. 하지만 전기차, 하이브리드차, 내연기관차에 대한 클래스를 생성할 때 동일한 속성과 행동을 가짐에도 불구하고 다른 이름의 속성과 행동을 정의할 수 있다는 것이다. 따라서 이러한 클래스는 자동차라는 클래스를 상속함으로써 속성과 행동의 이름을 통일할 수 있다.

 

추상클래스 선언

public abstract class Car {
    private String owner;
    
    public Car(String owner) {
    	this.owner = owner;
    }
    
    public void forward() {
    	System.out.println("전진합니다");
    }
    
    public void reverse() {
    	System.out.println("후진합니다");
    }
}

추상 클래스를 선언할 때에는 class 키워드 앞에 abstract 키워드를 붙여야한다.

추상 클래스는 new 연산자를 이용해서 객체를 생성하지 못하기 때문에 직접 생성자를 호출하지 못하지만, 자식 객체가 생성될때 추상 클래스 객체가 생성되므로 생성자가 반드시 필요하다.

public class HybridCar extends Car {
    private boolean isElectricMode;
    
    public HybridCar(String owner) {
    	super(owner);
        this.isElectricMode = false;
    }
    
    //전기 모드로 전환
    public void switchToElectric() {
        if (!isElectricMode) {
            isElectricMode = true;
        }
    }
}

extends 키워드를 붙여 Car 추상 클래스의 속성과 기능을 상속받도록 정의한다.

 

추상 클래스의 메서드와 구현

추상 클래스는 추상 메서드(구현이 없는 메서드)일반 메서드(구현이 있는 메서드)를 정의할 수 있다.

일반 메서드는 실체 클래스들이 공통적으로 갖는 메서드를 추상 클래스 내에 정의한 것이고, 추상 메서드는 메서드의 선언부만 있어 실행 내용을 실체 클래스에서 구현하는 메서드이다.

위에 작성한 예시로, 모든 자동차는 전진과 후진을 할 수 있어 이 부분을 일반 메서드로 구현할 수 있다.

하지만 전기차, 하이브리드차, 내연기관차는 다른 방식의 연료를 사용하므로 남은 연료를 가져오는 메서드는 다르게 구현될 것이다. 전기차는 전기를 연료로, 하이브리드차는 전기와 가솔린을, 내연기관차는 내연 연료를 사용하기 때문에 각 구현 내용이 다를 것이다. 따라서 이 부분은 추상 메서드로 정의하여, 실체 클래스에서 각자의 방식으로 구현할 수 있도록 해야한다.

 

일반메서드는 메서드 선언하고 구현 내용을 작성하면 된다.

자식 클래스는 일반 메서드를 재 정의할 수 있으며, 실제로 부모 클래스 타입으로 자식 객체를 생성하여 해당 메서드를 호출하였을 때 자식 클래스가 재정의한 일반메서드를 호출된다.

 

추상메서드는 abstract 키워드와 함께 메서드의 선언부만 있고 메서드 실행 내용인 중괄호가 없는 메서드이다.

추상클래스 설계시 하위 클래스가 반드시 실행 내용을 채우도록 강제하고 싶은 메서드가 있을 경우에 사용한다.

[public | protected] abstract 리턴타입 메서드이름(매개변수);

위에 작성한 예시로 추상 메서드를 정의해본다.

public abstract class Car {
    private String owner;
    
    public Car(String owner) {
    	this.owner = owner;
    }
    
    //일반 메서드
    public void forward() {
    	System.out.println("전진합니다");
    }
    
    //일반 메서드
    public void reverse() {
    	System.out.println("후진합니다");
    }
    
    //용량을 반환하는 추상 메서드
    public abstract int getRemainingCapacity();
}
public class HybridCar extends Car {
    private boolean isElectricMode;
    private int battery;
    private int gasoline;
    
    public HybridCar(String owner) {
    	super(owner);
        this.isElectricMode = false;
    }
    
    //일반 메서드 재정의
    @Override
    public void forward() {
       System.out.println("하이브리드 차 전진합니다");
    }
    
    //추상 메서드 구현
    @Override
    public int getRemainingCapacity() {
    	if (isElectricMode) { //전기차 모드일 경우
        	return this.battery;
        } else {
            return this.gasoline;
        }
    }
    
    public void switchToElectric() {
        if (!isElectricMode) {
            isElectricMode = true;
        }
    }
}

Car 에 추상메서드로 정의된 이 함수를 HybridCar 클래스에서 재정의 하여 구현을 진행한다.

 

추상 클래스 실행 예제

public class Main {
    public static void main(String[] args) {
    
        (1)
        HybridCar hybridCar = new HybridCar("김세영");
        hybridCar.switchToElectric();
    
        (2)
        Car car = new HybridCar("김세영");
        car.switchToElectric(); //호출할 수 없음
        car.forward(); //하이브리드 차 전진합니다 출력
    }
}

(1) HybridCar 타입의 변수를 선언하고, HybridCar의 생성자를 호출하여 객체를 생성하였다.

이 경우에 hybridCar는 부모의 속성과 기능 + 자신의 속성과 기능에 접근할 수 있다.

 

(2) Car 타입의 변수를 선언하고, HybridCar의 생성자를 호출하여 하이브리드차 객체를 생성하였다.

자식은 부모 타입으로 자동 타입 변환이 될 수 있고, 메서드가 정의되어있으면 재정의된 자식 메서드가 호출되는 다형성의 특징을 갖는다. 하지만 변수 타입이 부모 클래스인 Car 타입으로 선언되었기 때문에, Car 클래스에서 정의된 속성과 기능만 사용할 수 있고 Car 클래스에 정의되지 않은 자식 클래스만의 속성과 기능에는 접근할 수 없다.


인터페이스와 목적

객체의 사용법을 정의한 타입으로, 개발 코드와 객체가 서로 통신하는 접점 역할을 한다.

객체가 수행할 수 있는 행동을 정의할 목적으로 사용하는데, 모든 메서드는 추상적으로 기본적으로 구현이 없다. (java 8 이상부터는 디폴트 메서드와 정적 메서드를 지원한다.)

 

인터페이스 구성 멤버

상수 필드

인터페이스는 객체 사용법을 정의한 타입이므로 인스턴스 또는 정적 필드를 선언할 수 없고 상수 필드만 선언이 가능하다.

인터페이스에 상수 필드를 선언할 때 public static final 을 생략할 수 있으며, 컴파일 과정에서 자동으로 붙게 된다. 상수는 반드시 선언과 동시에 초기값을 지정해야하며 초기화를 하지 않으면 컴파일 에러가 발생한다.

 

추상 메서드

인터페이스로 호출된 메서드는 객체에서 실행되므로 실행 블록이 없는 추상 메서드로 선언한다.

인터페이스에 선언된 추상 메서드는 모두 public abstract 의 특성을 갖기 때문에, 따로 선언하지 않더라도 컴파일 과정에서 자동으로 붙게된다.

 

디폴트 메서드 (java 8 ~)

인터페이스 내에서 메서드의 기본 구현을 제공하는 메서드이다.

구현 클래스는 선택적으로 디폴트 메서드를 재정의 할 수 있다.

 

정적 메서드 (java 8 ~)

인터페이스 내에서 static 키워드를 사용하여 선언된 메서드로 인스턴스를 생성하지 않고 호출할 수 있다.

일반적으로 유틸리티 메서드를 정의할 때 사용한다.

public interface 인터페이스명 {
    //public static final 은 컴파일 시 자동으로 붙음
    int 변수명 = 10;
    
    //public abstract 는 컴파일 시 자동으로 붙음
    void 메서드명();
    
    //default 메서드
    default void 메서드명() {
    	
    }
    
    //static 메서드
    static void 메서드명() {
        
    }
}

 

인터페이스 선언과 구현

인터페이스를 선언할 때에는 interface 키워드를 사용하며, 추상 메서드를 다음과 같이 정의할 수 있다.

추상 메서드를 정의할 때 public abstract 는 생략이 가능하다.

public interface Hybridable {
    void switchToElectric();
    void switchToGasoline();
}

인터페이스를 구현하는 클래스에는 implements 키워드를 추가하고 인터페이스에 정의된 메서드를 재정의하여 구현하도록 한다.

public HybridCar implements Hybridable {
    private boolean isElectricMode;

    @Override
    public void switchToElectric() {
    	if (!isElectricMode) {
        	isElectricMode = true;
        }
    }
    
    @Override
    public void switchToGasoline() {
        if (isElectricMode) {
            isElectricMode = false;
        }
    }
}

또한 인터페이스는 다중 구현이 가능하다.

위에서 추상 클래스 선언 시, 운전에 대한 일반 메서드를 정의했었다. 근데 만약 자동차에 국한되지 않고 운전할 수 있는 다양한 객체가 존재하는 경우가 있을 것이다. 그렇다면 운전 기능을 인터페이스로 선언하는 것이 더 적합할 것이다.

public interface Drivable {
    public void forward();
    public void reverse();
}
public HybridCar implements Hybridable, Drivable {
    private boolean isElectricMode;

    @Override
    public void switchToElectric() {
    	if (!isElectricMode) {
        	isElectricMode = true;
        }
    }
    
    @Override
    public void switchToGasoline() {
        if (isElectricMode) {
            isElectricMode = false;
        }
    }
    
    @Override
    public void forward() {
        System.out.println("하이브리드 차 전진합니다");
    }
    
    @Override
    public void reverse() {
        System.out.println("하이브리드 차 후진합니다");
    }
}

추상 클래스와 인터페이스의 차이를 정리해본다.

 

추상 클래스는 클래스들의 공통적인 필드와 메서드를 추출해서 선언한 클래스로, 실체 클래스는 추상 클래스를 상속받아서 사용할 수 있다.

> abstract 키워드를 사용해서 클래스를 선언한다.

> 상속의 관계를 갖기 때문에 다중 상속이 불가능하다.

> 일반메서드와 추상메서드를 정의할 수 있다.

> 필드를 가질 수 있고, 필드에 접근제어자를 사용할 수 있다.

 

인터페이스는 메서드의 시그니처만 정의하고 구현을 제공하지 않는 객체의 사용 방법을 정의한 타입이다.

> interface 키워드를 사용해서 클래스를 선언한다.

> 여러 인터페이스를 동시에 구현할 수 있어 다중 상속이 가능하다.

> 메서드의 구현을 제공하지 않으며, 일반적으로 모든 메서드가 추상적이다. (default 메서드와 static 메서드를 포함할 수 있음)

> 상수 필드만 선언할 수 있고, 접근제어자는 항상 public 이다.

 

인터페이스에 대해 더 알아보기

2024.10.20 - [Language/Java] - 인터페이스에 대하여

 

인터페이스에 대하여

인터페이스는 객체의 사용 방법을 정의하는 데 사용되는 추상 타입이다.그럼 왜 인터페이스를 사용해야하고, 이점은 무엇이 있는지에 대해 알아보려고 한다.인터페이스의 다중 구현과 다중 상

yeongnius.tistory.com

 

 

'Language > Java' 카테고리의 다른 글

상속에 대하여  (0) 2024.10.31
인터페이스에 대하여  (0) 2024.10.27
싱글톤(Singleton)에 대하여  (0) 2024.10.20
상속과 인터페이스의 다형성  (0) 2024.10.20
인스턴스 멤버와 정적 멤버  (0) 2024.10.18
공지사항
최근에 올라온 글
최근에 달린 댓글
Total
Today
Yesterday
링크
«   2025/04   »
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
글 보관함