서론
자바의 추상 클래스, 추상 메서드, 인터페이스에 대해 공부하였고 실습과 개념정리를 통해 다시 한번 제데로 이해할 수 있는 시간이였다. 그리고 옵시디언을 이용하여 내용을 정리하고 있는데 노트간에 링크 기능이 마음에든다...
본론
추상 클래스
추상 클래스는 이름 그대로 추상적인 개념을 제공하는 클래스이다.
예를 들어 "동물" 이라는 개념을 생각해 볼 수 있다. 동물은 구체적인 형태나 실체가 존재하지 않고 개념적으로 존재하며, 모든 동물이 공통적으로 가지는 속성과 행동을 정의하는 개념이다.
추상적인 개념은 구체적인 형태나 실체가 존재하지 않고 여러가지 공통적인 특징을 모아 놓은 것을 의미한다.
이 추상적인 개념을 바탕으로 강아지나, 고양이 처럼 구체적인 형태나 실체가 존재하는 것을 만들 수 있다.
이처럼 추상적인 개념을 컴퓨터 프로그램내에서 사용하기 위하여 추상 클래스를 사용한다. 동물은 추상적인 개념으로 구체적인 형태나 실체가 없다. 마찬가지로 자바의 추상 클래스도 구체적인 형태나 실체를 가질 수 없지만 공통적으로 가지는 속성과 행동을 정의한다. 추상 클래스는 기본적인 틀을 제공하며, 이를 기반으로 구체적인 클래스들이 실제로 어떤 속성과 행동을 가질 지 정의할 수 있다.
추상 클래스는 클래스를 선언할 때 `abstract` 키워드를 붙여 사용할 수 있다. 추상 클래스는 일반 클래스와 동일하지만 직접 인스턴스를 생성할 수 없다는 제약이 추가된것이다. 그렇기 때문에 추상 클래스는 주로 다른 클래스들이 상속받아 사용할 수 있는 기본적인 틀을 제공하는 역할을한다.
그럼 왜 추상 클래스를 사용할까?
추상 클래스는 다른 클래스들이 공통적으로 가져야할 기능이나 속성을 미리 정의한다 하였다. 이렇게 하면 구체적인 클래스들은 같은 기본 틀을 따르면서 자신만의 세부사항을 추가할 수 있게된다.
예를 들어 "동물" 이라는 추상 클래스를 만든다고 생각해보자. 이 추상 클래스는 "걷기" 나 "먹기" 와 같은 기본 행동을 정의할 수 있다. 하지만 "동물"이라는 추상 클래스만으로는 구체적인 동물, 예를 들어 "강아지" 나 "고양이"를 만들 수 없다. 대신 "강아지" 와 "고양이" 같은 구체적인 동물 클래스는 "동물" 이라는 추상 클래스를 상속받아 기본 행동과 속성을 따라야 하면서 자신만의 특성도 추가할 수 있게 된다.
이렇게 추상 클래스를 사용하게되면 여러 자식 클래스들이 공통된 규칙을 따르게하고 각 자식 클래스는 자신만의 세부사항을 추가할 수 있게 되어 코드의 일관성과 재사용성을 높일 수 있다.
추상 클래스는 일반 클래스와 마찬가지로 속성과 기능을 가질수 있으며 추가적으로 추상 메서드를 가질수 있다. 추상 메서드를 포함하고 있으면 반드시 추상 클래스로 선언해주어야 한다. 왜냐하면 추상 메서드는 추상적인 기능이나 행동을 의미하므로 실체를 가질 수 없기 때문이다. 따라서 추상 클래스로 선언하여 구체적인 자식 클래스에게 추상 메서드를 구현하도록 강제하는 역할을한다.
추상 메서드
추상 메서드도 추상 클래스와 비슷한 개념으로, 추상 메서드는 추상적인 개념을 제공하는 메서드이다.
즉 구체적인 구현 없이 이름과 기능만 정의하는 메서드이다. 예를 들어 "동물"이라는 개념을 생각해보자. 동물이라는 말은 고양이, 개, 사자 등의 모든 동물들이 공통적으로 가지는 속성이나 행동 (기능)을 의미하지만, 특정한 동물을 가리키지 않는다. 여기서 메서드란 이 동물이 할 수 있는 행동, 예를 들어 "뛰기" 나 "소리내기" 등을 의미한다.
즉, 추상 메서드는 "동물은 뛸 수 있다" 라고 정의하지만 어떻게 뛰는지는 말해주지 않는다. 이처럼 추상 메서드는 기본적인 행동이나 기능의 '틀' 제공하며 이를 기반으로 이를 바탕으로 구체적인 행동이나 기능을 각 동물별로 정의할 수 있다.
추상 메서드는 메서드 앞에 `abstract` 키워드를 사용하여 선언할 수 있다. 추상 메서드는 메서드 본문이 존재하지 않으므로이 미완성 메소드이다. 따라서 이 추상 메서드를 가지고 있는 클래스도 추상 클래스로 선언해주어야 한다. 추상 메서드는 완성되지 않은 기능을 의마히기 때문에 직접 인스턴스를 생성할 수 없기 때문이다. 대신 구체적인 클래스가 이 추상메서드를 상속받아 직접 구현해야한다.
그럼 왜 추상 메서드를 사용할까?
먼저 추상 메서드는 추상적인 개념에서 공통적으로 가지는 행동을 의미하며, 구체적인 클래스에서 실제로 어떻게 구현될지에 대한 틀을 제공한다. 예를 들어, "동물"이라는 추상적인 개념에서 동물은 공통적으로 "걷다", "먹다"와 같은 행동을 가지지만, 구체적인 동물인 강아지, 고양이, 새 등의 특정 동물은 이러한 행동을 각자의 방식으로 다르게 수행한다. 추상 메서드는 이러한 공통된 행동의 틀을 제공하며 구체적인 클래스에서 이 메서드들을 자신의 방식에 맞게 구현하도록 강제하는 역할을 한다.
추상 클래스와 추상 메서드 사용 해보기
추상 클래스와 추상 메서드를 이용하여 예제를 위해 코드를 작성해 보았다. Animal 클래스는 모든 동물들이 공통적으로 가지는 속성과 기능을 정의한다. 따라서 Animal 클래스에는 모든 동물들이 공통적으로 가지는 이름과 나이, 그리고 eat() 및 move()와 같은 기본적인 기능을 정의하였다. 모든 동물은 먹는다는 행위를 수행하지만, 움직이는 방식은 동물에 따라 다를 수 있다. 예를 들어, 어떤 동물은 두 발로 걷거나 네 발로 걸을 수 있고, 또 어떤 동물은 날 수도 있다. 따라서 move() 메서드는 구체적인 구현이 필요하므로, 추상 메서드로 선언하여 자식 클래스에서 이를 구체적으로 정의하도록 강제하였다.
또한 Animal 클래스는 추상 클래스로 실체가 없기 때문에 직접 인스턴스를 생성할 수 없다. 따라서 구체적인 클래스가 이를 상속받아 사용해야하고 기본적인 속성과 행동의 틀을 따르며 각자의 특징에 맞는 세부 구현을 추가해야한다.
abstract class Animal {
String name;
int age;
void eat(String food) {
System.out.println(food + "를 먹는다.");
}
abstract void move();
}
구체적인 동물을 만들기 위해 Dog 클래스를 선언하였다. Dog 클래스는 Animal 클래스를 상속받아 구현되며, 추상 클래스 Animal의 추상 메서드인 `move()` 를 오버라이드하여 강아지의 이동 방식을 정의하였다.
class Dog extends Animal {
@Override
void move() {
System.out.println("강아지는 네 발로 움직인다.");
}
}
다음은 `Bird` 클래스이다. 마찬가지로 Animal 클래스를 상속받아 구현하였으며 새는 날거나 두발로 걷기 때문에 `move()` 메서드를 오버라이드 하여 새의 이동 방식을 재정의하였다.
class Bird extends Animal {
@Override
void move() {
System.out.println("새는 두발로 걷거나 날 수 있다.");
}
}
public class Main {
public static void main(String[] args) {
Animal dog = new Dog();
dog.eat("사료");
dog.move();
System.out.println();
Animal bird = new Bird();
bird.eat("모이");
bird.move();
}
}
추상적인 개념인 "동물"을 추상 클래스를 이용하여 코드를 구현할 수 있었고 이러한 구조로 통해 코드의 재사용성을 높이고, 공통된 규칙을 따르면서도 각 동물의 특성에 맞는 행동을 구현할 수 있었다. 추상 클래스를 사용함으로써, 공통적인 기능을 정의하고, 자식 클래스에서 구체적인 세부 사항을 구현하는 유연한 설계가 가능하다는것을 직접 실습 해보았다.
인터페이스
나만의 언어로 정리 > 인터페이스는 자판기와 같다 자판기는 공장에서 제조되며 기본적인 구조, 즉 색상, 크기, 모양과 같은 속성을 가지고 있고, 돈을 넣고 제품을 뽑을 수 있는 기능을 제공하며 사용자가 원하는 대로 제품을 채울 수 있는것 처럼 인터페이스는 클래스가 따라야 할 메서드의 목록을 정의하고, 실제 구현은 클래스에서 자유롭게 정의할 수 있도록 한다 이렇게 함으로써, 인터페이스는 클래스들이 공통된 규칙을 따르도록 하면서도 각 클래스가 특정 세부 사항을 자유롭게 구현할 수 있게 한다.
인터페이스는 interface 키워드를 통해 선언할 수 있으며, 인터페이스의 멤버 변수는 자동으로 public, static, final로 설정된다 이는 인터페이스의 멤버 변수는 상수로 취급되며, 값을 변경할 수 없다는 것을 의미한다. 이 특성은 인터페이스가 기본적으로 공통된 상수 값을 정의하고, 모든 구현 클래스에서 동일하게 사용할 수 있도록 보장하는 역할을한다. 또한 모든 메서드가 추상 메서드로 정의되어 있으며 다중 상속을 지원한다.
추상 클래스와 인터페이스의 차이점이 뭘까
먼저 추상 클래스는 추상적인 개념의 공통적인 속성과 기능을 정의한다. 추상 클래스에서 일부 기능을 직접 구현을 할 수 있고 또 다른 기능는 추상 메서드를 사용하여 구체적인 클래스에서 구현을 강제하도록 할 수 있다.
반면, 인터페이스는 추상 클래스와 매우 유사하지만 메서드의 시그니처만 정의하고 기본적인 구현을 제공하지 않는다. 인터페이스는 클래스가 반드시 구현해야 하는 메서드를 정의하며, 클래스가 이 인터페이스를 구현하면 특정 기능을 반드시 포함하도록 강제할 수 있다. 여러 인터페이스를 구현할 수 있기 때문에 다중 상속의 역할을 하기도 하며, 다양한 클래스가 동일한 메서드를 서로 다른 방식으로 구현할 수 있게 한다.
인터페이스를 활용하여 예제 코드를 작성해였다. Keyboard 인터페이스를 만들었고, 모든 키보드가 공통적으로 가지고 있는 기능을 정의했다. 인터페이스에 작성된 모든 메서드는 이를 구현하려는 클래스에서 반드시 구현해야 한다.
interface Keyboard {
void input(String text);
void powerOn();
void powerOff();
}
Keyboard 인터페이스를 구현하는 StandardKeyBoard 클래스를 구현해 보았다.
class StandardKeyboard implements Keyboard {
@Override
public void input(String text) {
System.out.println("입력된 텍스트: " + text);
}
@Override
public void powerOn() {
System.out.println("전원 On");
}
@Override
public void powerOff() {
System.out.println("전원 Off");
}
}
Keyboard 인터페이스를 구현하는 GamingKeyBoard 클래스를 구현하였고 추가로 매크로 기능을 추가해보았다.
class GamingKeyboard implements Keyboard {
@Override
public void input(String text) {
System.out.println("입력된 텍스트: " + text);
}
@Override
public void powerOn() {
System.out.println("전원 On");
}
@Override
public void powerOff() {
System.out.println("전원 Off");
}
// 게임용 키보드 에는 매크로 버튼이 추가되었다.!
public void enableMacro() {
System.out.println("매크로 기능이 활성화되었습니다.");
}
}
이렇게 인터페이스를 사용하면 여러 종류의 키보드가 공통된 기능을 가지면서도, 각자의 특성에 맞게 기능을 구현할 수 있다. 추상 클래스와 달리 인터페이스의 모든 메서드를 반드시 구현해야하며 나중에 키보드의 표준이 바뀌어 새로운 기능을 추가해야되면 모든 키보드도 이에 맞춰 구현을 해야한다. 따라서 인터페이스는 적절한 제약을 통해 다양한 클래스가 일관된 방식으로 동작하도록 보장한다.
다이아몬드 문제
자바는 왜 다중 상속을 지원하지 않을까?
예를 들어, 회사에 두 개의 부서가 있다고 해보자 회사는 영업부서 와 마케팅부서가 존재하며 이 두 부서에는 공통적으로 "보고서 작성"이라는 기능이 있다.
- 영업부서 클래스에는 "영업 보고서를 작성"하는 메서드가 있다
- 마케팅부서 클래스에는 "마케팅 보고서를 작성"하는 메서드가 있다.
이제 한 직원이 두 부서에서 일을 한다고 가정해자요 이 직원을 표현하는 직원 클래스가 영업부서, 마케팅 부서 를 동시에 상속받으면 어떤 문제가 발샐할까? 바로 직원 클래스에서 "보고서 작성" 메서드를 실행해야될지 애매해지기 때문이다. 이문제가 바로 다이아몬드 문제이다.
따라서 자바는 클래스의 다중 상속을 허용하지 않는다. 그럼 왜 인터페이스는 됌?
인터페이스는 메서드의 시그니처(이름, 매개변수, 반환 타입) 만 선언하고 실제 구현은 포함하지 않기 때문이다. 인터페이스는 기본적으로 어떤 기능이 필요하다고만 정의하고 그 기능이 실제로 어떻게 동작할지는 구현할 클래스가 책임진다.
이렇게 하면 다중 인터페이스를 구현할 때 충돌이 발생하지 않는다. 구현받은 여러 인터페이스의 중복되는 메서드가 존재해도 메서드의 실제 구현 내용은 해당 클래스가 구현하는 개발자가 직접 정의하기 떄문이다. 결국, 인터페이스의 메서드가 서로 겹치더라도, 이를 구현하는 클래스가 각 메서드의 구체적인 동작을 명확하게 정의하기 때문에 충돌을 방지할 수 있다.
결론
추상 클래스와 추상 메서드의 대해 개념을 알아갈 수 있었고 추상 클래스와 인터페이스의 차이점을 알 수 있었고 자바의 다중상속이 안되는이유와 인터페이스의 다중 구현이 가능한 이유를 알 수 있었다.
'TIL' 카테고리의 다른 글
[TIL | 2024-08-29] JVM, 자바 메모리 구조 (0) | 2024.08.29 |
---|---|
[TIL | 2024-08-28] 예외, 예외 처리 (0) | 2024.08.29 |
[TIL | 2024-08-26] 상속, 오버라이딩 (0) | 2024.08.26 |
[TIL | 2024-08-24] 메서드, 프로그램의 기능적 분할, 객체 지향 프로그래밍 (0) | 2024.08.23 |
[TIL | 2024-08-23] 배열, 반복문 (0) | 2024.08.22 |