abstract class
객체 지향 프로그래밍에서 추상화 작업을 하기 위해 사용하는 클래스이다.
추상화의 의미는 간단하게,
해당 구조나 기능을 구체적으로 알지 못해도 (추상적으로) 제공된 구조를 통해 간편하게 사용할 수 있게 해주는 것
그러므로 추상 클래스는 “추상화”를 클래스에 접목시킨 것입니다.
내가 생각하는 추상 클래스
추상 클래스는 미완성 설계도와 비슷하다.
이것이 무슨 뜻이냐면,
추상 클래스를 통해 밑그림(추상화)을 그리고 이를 바탕으로 관련된 새로운 객체를 만들 때 주어진 특징(기능)을 알 수 있습니다.
abstract class 사용 이유
- 구조적으로 객체를 설계할 수 있음
- 프로그램의 유지보수성이 향상됨
- 확장하는 요구사항에 맞게 간편하게 확장시킬 수 있음
abstract class 문법
1. 추상 메서드 - abstract
Java에서는 abstract 키워드를 클래스명과 메서드명 옆에 붙여 추상 클래스와 추상 메서드임을 명시합니다.
// 추상 클래스
public abstract class Shape {
private String type;
// 추상 메서드
public abstract void draw();
}
추상 메서드는 작동 로직은 따로 없고 이름만 존재합니다.
→ 메서드 선언부만 작성한 미완성 상태, 이를 구현체에서 구현하게 함으로써 각 객체별 기능을 다양하게 유지하게 해 줍니다.
2. 추상 클래스 생성자
public abstract class Animal {}
Animal animal = new Animal(); // ERROR
- 추상 클래스는 new 생성자를 통해 인스턴스 객체를 직접 만들 수 없습니다.
- 추상 클래스는 상속 구조에서 부모 클래스 역할로만 사용되며, 상속한 자식 클래스를 인스턴스화하여 사용해야 함
3. super()를 통한 추상 클래스 생성자 사용
추상 클래스는 직접적인 인스턴스화가 불가능할 뿐, super() 메서드를 통해 부모 클래스 생성자 호출은 가능합니다.
abstract class Shape {
public String type;
// 추상 클래스 생성자
public Shape(String type) {
this.type = type;
}
public abstract void draw();
}
class Figure extends Shape {
public String name;
public Figure(String type1, String type2) {
super(type1); // 부모 추상 클래스 생성자 호출
this.name = type2;
}
@Override
public void draw() { ... }
}
public class main {
public static void main(String[] args) {
Figure f = new Figure("polygon", "square");
f.name;
f.type;
}
}
사용하는 상황
부모 추상 클래스 생성자 실행에 있어 인자를 주어 제어하는 경우
abstract class 활용법
- 공통 멤버의 통합을 통해 중복 제거
class Marine {
int x, y;
void move(int x, int y) {} // 지정된 위치로 이동
void stop() {} // 현재 위치에 정지
void stimPack() {} // 고유 능력 스팀팩 사용
}
class Tank {
int x, y;
void move(int x, int y) {} // 지정된 위치로 이동
void stop() {} // 현재 위치에 정지
void siegeMode() {} // 고유 능력 시즈 모드 사용
}
class DropShip {
int x, y;
void move(int x, int y) {} // 지정된 위치로 이동
void stop() {} // 현재 위치에 정지
void loadUnload() {} // 고유 능력 탑승 사용
}
- 위 3개의 객체가 같은 기능을 담당하는 객체라면, 공통된 기능을 부모 클래스로 묶어 상속받아 코드의 중복 제거와 코드 재사용성을 증대시킬 수 있습니다.
abstract class Unit {
int x, y;
abstract void move(int x, int y); // 지정된 위치로 이동
void stop() {} // 현재 위치에 정지
}
class Marine extends Unit{
void move(int x, int y) {
System.out.println("걸어서 이동");
}
void stimPack() {} // 고유 능력 스팀팩 사용
}
class Tank extends Unit{
void move(int x, int y) {
System.out.println("굴러서 이동");
}
void siegeMode() {} // 고유 능력 시즈 모드 사용
}
class DropShip extends Unit{
void move(int x, int y) {
System.out.println("날아서 이동");
}
void loadUnload() {} // 고유 능력 탑승 사용
}
- move() : 각 자식 클래스마다 이동하는 로직이 다르므로 추상 메서드로 선언하여, 자식 클래스에서 오버라이딩 하게 하였습니다.
- “움직인다”라는 기능만 수행할 수 있다면, 각 객체는 다른 방법으로 재정의 될 수 있음을 의미합니다.
- stop() : 상속받는 모든 객체에 동일한 기능(명령)을 주기 위해 추상 메서드로 선언하지 않았습니다.
- 미완성 설계도로써, 어느 정도의 제한을 설정할 수 있다.
abstract class 특징
- 구현의 강제성
abstract class Unit {
int x, y;
abstract void move(int x, int y); // 지정된 위치로 이동
void stop() {} // 현재 위치에 정지
}
class Battlecruiser extends Unit {
void yamato() {} // 고유 능력 야마토 사용
}
class Marine extends Unit { ... }
class Tank extends Unit { ... }
class DropShip extends Unit { ... }
깜빡하고 상속한 추상 클래스의 메서드를 재정의 하지 않으면 치명적인 버그가 일어날 수 있습니다.
하지만, 추상 클래스는 큰 문제가 되지 않습니다. 왜냐하면 컴파일 단계에서 빨간 줄 에러를 통해 추상 메서드 move()를 재정의 하라고 에러 메시지를 알려줍니다.
→ 일반 클래스로도 상속 관계를 맺을 수 있지만 이러한 점 때문에 추상화 작업과 컴파일 단계에서의 오류를 제공하는 추상 클래스와 인터페이스를 사용합니다.
Interface
실생활에서의 인터페이스 쓰임
사용자가 기기를 쉽게 동작시키는데 도움을 주는 상호작용 시스템
(서로 다른 두 개의 시스템, 장치 사이에서 정보나 신호를 주고받는 경우의 경계면이라 합니다.)
예시로 윈도우 인터페이스 (리눅스 OS 같은 경우 터미널 명령어를 통해 파일을 실행 및 조작해야 함)
프로그래밍에서의 인터페이스 쓰임
객체의 인스턴스 메서드를 이용하는 입장에서 “그 객체의 내부 구현이 어떻든 깊이 이해할 필요 없이 원하는 메서드만 호출하고 결과 값을 받게 해주는 기능”입니다.
Interface 역할
- 프레임워크의 내부 구성 학습 없이, 그저 지원해 주는 메서드를 이용하여 개발이 가능하다.
- 규격과 스펙을 정의하여 유지보수성을 높일 수 있다.
Interface 정의
interface TV {
// public static final 생략 가능
int MAX_VOLUME = 10;
int MIN_VOLUME = 10;
// public abstract 생략 가능
void turnOn();
void turnOff();
void changeVolume(int volume);
void changeChannel(int channel);
}
- 인터페이스에 필드를 선언하기 위해서는 상수(final)로만 정의가 가능합니다.
- public static final, public abstract 키워드(제어자)는 생략이 가능합니다.
- 생략된 제어자는 컴파일 시에 컴파일러가 자동으로 추가해 줍니다.
Interface 구현
- 추상 클래스가 상속을 통해 완성되는 것처럼 인터페이스는 구현부에서 구현됨으로써 완성됩니다.
- 인터페이스를 구현했다면, 자식 클래스에서 인터페이스가 포함하고 있는 추상 메서드를 반드시 구현해주어야 합니다.
인터페이스 특징
- 다중 구현이 가능합니다. (abstract class는 다중 상속이 불가능)
- 자식 클래스에서 클래스 상속과 인터페이스 구현이 동시에 가능합니다.
interface Animal {
public abstract void cry();
}
interface Pet {
public abstract void play();
}
class Tail {
// ...
}
// 클래스와 인터페이스를 동시에 상속
class Cat extends Tail implements Animal, Pet {
public void cry() {
System.out.println("냐옹냐옹!");
}
public void play() {
System.out.println("쥐 잡기 놀이하자~!");
}
}
인터페이스가 extends 키워드 대신 implements라는 '구현'이라는 키워드를 사용하는 이유
상속은 클래스 간의 부모 - 자식 관계를 연관시키는데 의미가 중점 된다면,
구현은 클래스를 확장시켜 다양히 이용하는데 중점이 되기 때문입니다.
Interface 일부 구현
자식 클래스가 구현하는 인터페이스의 메서드 중 일부만 구현하려고 한다면, abstract class로 선언해야 합니다.
→ 인터페이스의 추상 메서드를 그대로 상속받기 때문에, 인터페이스를 상속한 클래스에서 메서드 구현을 안 한다면, 이는 추상 메서드를 가진 추상 클래스가 됩니다.
interface Animal {
void walk();
void run();
void breed();
}
// Animal 인터페이스를 일부만 구현하는 포유류 추상 클래스
abstract class Mammalia implements Animal {
public void walk() { ... }
public void run() { ... }
// public void breed() 는 자식 클래스에서 구체적으로 구현하도록 일부로 구현하지 않음 (추상 메서드로 처리)
}
class Lion extends Mammalia {
@Override
public void breed() { ... }
}
Interface 자체 상속
인터페이스 자체를 확장시키고 싶으면 extends 제어자를 통해 다른 인터페이스를 상속합니다.
클래스와 달리 인터페이스끼리는 다중 상속이 가능합니다.
→ 자식 인터페이스는 부모 인터페이스에 정의된 멤버, 메서드를 모두 상속받습니다.
추가 지식 인터페이스에 클래스를 상속하는 행위는 불가능하다. 왜냐하면, 인터페이스는 클래스와는 달리 Object 클래스가 최고 조상이 아니기 때문이다.
참고자료
'JAVA' 카테고리의 다른 글
[Java] Java Shutdown Hook 이란 - System.exit() (2) | 2024.09.25 |
---|---|
[Java] 자바 predicate, consumer, supplier, function 이해하기 - 함수형 인터페이스 이해하기 (3) | 2024.09.22 |
[Java] Java of, from, parse 정적 팩토리 메서드 이해하기 - Method Naming Convention (0) | 2024.09.19 |
[Java] Java Switch와 if/else 성능 비교 - jump table, lookup table (1) | 2024.09.17 |
[Java] 자바 인코딩, 디코딩, base64 인코딩, 디코딩 처리하기 (1) | 2024.09.14 |