Java #1 객체지향 프로그래밍: 캡슐화, 추상화, 다형성, 상속 #
#2025-09-01
목차 #
- 캡슐화
- 추상화
- 다형성
- 상속
- 공통 특성: 인터페이스와 구현의 분리
#
1. 캡슐화 #
#1 개념 및 목적
- 개념
- 객체지향 프로그래밍에서 객체의 속성(필드)을 외부로부터 숨기고, 공개된 메서드(getter/setter)를 통해서만 접근하도록 만드는 원칙
- 필드를 private으로 선언하고, 외부에서 직접 접근하지 못하게 제한하고, public 메서드인 getter와 setter를 제공해 값을 읽거나 수정할 수 있도록 한다. setter 내부에는 유효성 검사 로직을 넣어 잘못된 값이 들어오는 것을 막을 수도 있다.
- 목적
- 데이터 보호: 잘못된 값이 직접 들어가는 것을 막고, setter 내부에서 규칙을 강제함으로써 객체의 상태를 안정적으로 유지
- 정보 은닉: 내부 구현이 어떻게 되어 있는지는 숨겨 두고, 외부에는 단순한 사용 방법만 제공함으로써 객체 사용자가 불필요한 복잡성을 신경 쓰지 않도록 한다.
- 유지보수와 확장성: 내부 로직이 바뀌더라도 외부 인터페이스(getter/setter)가 같으면 사용하는 코드는 수정할 필요가 없으므로 프로그램 전체의 안정성이 높아지고 유지보수가 쉬워진다.
#
#2 샘플 코드
class Stock {
// 1. 필드는 외부에서 직접 접근 불가능 (private)
private String name;
private double price;
// 생성자
public Stock(String name, double price) {
this.name = name;
setPrice(price); // setter 사용 → 유효성 검사 포함
}
// 2. Getter (읽기 전용)
public String getName() {
return name;
}
public double getPrice() {
return price;
}
// 3. Setter (쓰기 전용, 유효성 검사 추가)
public void setPrice(double price) {
if (price > 0) {
this.price = price;
} else {
System.out.println("❌ 잘못된 가격: " + price);
}
}
}
public class EncapsulationExample {
public static void main(String[] args) {
// 정상적인 객체 생성
Stock s1 = new Stock("스칼라 AI", 17000);
System.out.println(s1.getName() + " 현재가: " + s1.getPrice());
// setter를 통한 가격 변경 (올바른 값)
s1.setPrice(18000);
System.out.println("업데이트 후 가격: " + s1.getPrice());
// setter를 통한 잘못된 값 입력 (음수)
s1.setPrice(-5000); // → 유효성 검사에서 거부
System.out.println("최종 가격: " + s1.getPrice());
}
}
class Stock
- private String name;
- 주식의 이름(예: “스칼라 AI”)
- private이기 때문에 클래스 외부에서는 s1.name처럼 직접 접근 불가
- 주식의 이름(예: “스칼라 AI”)
- private double price;
- 주식의 가격을 저장하는 변수
- private이기 때문에 클래스 외부에서는 직접 접근 불가
- public Stock
- this.name = name;
- 생성 시 입력된 이름을 객체의 name 에 저장
- setPrice(price);
- 가격은 바로 대입하지 않고 setPrice() 메서드를 통해 저장
- this.name = name;
- Getter
- getName(): 주식 이름
- getPrice(): 주식 가격
- Setter
- if (price > 0): 유효성 검사
- 올바른 가격(0보다 큰 수)이면 저장, 잘못된 값이면 거부하고 메시지를 출력하기.
- if (price > 0): 유효성 검사
public class EncapsulationExample
- public static void main(String[] args)
- Stock 객체를 실제로 만들어서 테스트하는 클래스.
- Stock s1 = new Stock(“스칼라 AI”, 17000);
- “스칼라 AI"라는 이름과 17000이라는 가격으로 객체 생성.
- 생성자 내부에서 setPrice(17000)이 호출되므로 유효성 검사가 통과되므로 저장된다.
- “스칼라 AI"라는 이름과 17000이라는 가격으로 객체 생성.
- System.out.println(s1.getName() + " 현재가: " + s1.getPrice());
- getName()과 getPrice()로 값을 출력
- s1.setPrice(18000);
- setter를 통한 가격 변경
- setPrice(18000)은 유효성 검사를 통과하므로 price가 18000으로 업데이트된다.
- setter를 통한 가격 변경
- setPrice(-5000)
- setter 내부 조건문이 거부예정.
- “잘못된 가격: -5000” 메시지만 출력되고, price 값은 바뀌지 않고, getPrice()로 확인하면 여전히 이전 값 18000이 유지된다.
- setter 내부 조건문이 거부예정.
#
#4 의문점
캡슐화의 의미?
- 중요한 데이터는 직접 노출하지 않고 private으로 은닉하며, getter/setter 같은 메서드를 통해서만 접근하도록 만들기.
this.price = price; 하지않고 setPrice(price) 한 이유?
- 이 값이 올바른지 아닌지 검사하는 로직을 넣기위해서.
- 생성자에서 this.price = price;를 바로 쓰면 잘못된 값도 그대로 들어와 버릴 수 있다. 예를 들어 new Stock(“삼성”, -1000) 같은 유효하지않은 객체가 생성될수있는데 setPrice(price);를 쓰면 생성되는 순간에 그 값이 유효한지 검사하고 잘못된 값은 차단할 수 있다.
- 결론
- 생성자 안에서 직접 대입하지 않고 setter를 호출하면 내부 로직이 항상 같은 규칙을 따르게 함으로써 어디서 값을 넣든지 간에 일관성과 안전성이 유지된다.
#
2. 추상화 #
#1 개념 및 목적
- 개념
- 객체지향 프로그래밍에서 복잡한 시스템을 단순화하기 위해 핵심적인 개념과 동작만 남기고 불필요한 세부사항을 감추는 원칙
- 추상 클래스와 인터페이스
- 추상 클래스: 공통된 속성과 기본 동작을 정의하면서, 일부 메서드를 추상 메서드로 남겨 자식 클래스가 반드시 구현하도록 한다.
- 인터페이스: 특정 기능에 대한 규약을 정의하며, 이를 구현하는 클래스가 해당 메서드를 구체적으로 작성하도록 강제한다.
- 목적
- 복잡성 단순화: 사용자나 개발자는 내부의 복잡한 구조를 알 필요 없이, 제공되는 메서드 시그니처만 보고 객체를 사용할 수 있다.
- 코드의 유연성과 유지보수성 향상: 외부에서 바라보는 표면(메서드 선언)만 일정하게 유지하면 내부 구현은 자유롭게 변경하거나 최적화할 수 있다.
- 일관성과 확장성 확보: 추상 클래스는 공통 뼈대를 재사용하게 해주고, 인터페이스는 다양한 클래스들이 동일한 규약을 따르도록 만들어 여러 객체를 일관된 방식으로 다룰 수 있게 한다. 이를 통해 협업과 테스트가 쉬워지고, 새로운 기능 확장이 용이해진다.
#
#2 샘플 코드
// 추상 클래스: 공통 자산
abstract class Asset {
protected String name;
protected double price;
public Asset(String name, double price) {
this.name = name;
this.price = price;
}
// 자식 클래스들이 반드시 구현해야 하는 추상 메서드
public abstract void printInfo();
}
// 인터페이스
interface Valuable {
void printInfo(); // 반드시 구현해야 함
// default 메서드 (인터페이스도 구현 제공 가능)
default void updatePrice(double price) {
System.out.println("가격을 " + price + "원으로 업데이트했습니다.");
}
}
// 일반주 클래스: 추상 클래스 상속 + 인터페이스 구현
class Stock extends Asset implements Valuable {
public Stock(String name, double price) {
super(name, price);
}
@Override
public void printInfo() {
System.out.println("[일반주] 종목: " + name + " / 현재가: " + price + "원");
}
}
// 우선주 클래스: 추상 클래스 상속 + 인터페이스 구현
class PreferredStock extends Asset implements Valuable {
private double dividendRate;
public PreferredStock(String name, double price, double dividendRate) {
super(name, price);
this.dividendRate = dividendRate;
}
@Override
public void printInfo() {
System.out.println("[우선주] 종목: " + name +
" / 현재가: " + price + "원" +
" / 배당률: " + dividendRate + "%");
}
}
// 실행 클래스
public class AbstractionExample {
public static void main(String[] args) {
// 추상 클래스는 직접 인스턴스화 불가 → 자식 클래스를 통해 사용
Asset samsung = new Stock("삼성전자", 72000);
Asset lgPref = new PreferredStock("LG전자우", 83000, 4.5);
// 다형성: 같은 printInfo() 호출이지만 실제 객체에 따라 다르게 동작
samsung.printInfo();
lgPref.printInfo();
// 인터페이스 default 메서드 사용
Valuable v = new Stock("카카오", 57000);
v.updatePrice(58000);
}
}
추상 클래스 Asset
- abstract class Asset
- 추상 클래스 정의
- protected String name, protected double price
- name, price 필드
- protected 접근제어자 사용해서 같은 패키지 내부와 자식 클래스에서만 접근 가능하게한다.
- public Asset(String name, double price) {this.name = name; this.price = price;}
- name과 price를 초기화
- public abstract void printInfo();
- printInfo()는 추상 메서드로 선언되어 있고 구현은 없다.
- sset을 상속받는 자식 클래스들은 반드시 printInfo()를 구현해야한다 즉 Asset은 “공통 자산"이라는 추상적인 개념만 정의하고 구체적인 세부 내용은 자식 클래스에서 맡기는 구조.
- printInfo()는 추상 메서드로 선언되어 있고 구현은 없다.
인터페이스 Valuable
- interface Valuable
- 객체가 가져야 할 행동 규약
- void printInfo();
- 선언만 되어 있고 구현은 없음. 인터페이스를 구현하는 클래스는 반드시 이 메서드를 작성해야한다.
- default void updatePrice(double price) {System.out.println(“가격을 " + price + “원으로 업데이트했습니다.”);}
- 기본 구현: “가격을 업데이트했습니다”라는 메시지를 출력하기.
Stock 클래스
- class Stock extends Asset implements Valuable
- 추상 클래스 Asset을 상속하고 인터페이스 Valuable을 구현한다.
- public Stock(String name, double price) {super(name, price);}
- 생성자가 부모 클래스 Asset의 생성자를 호출해 name, price를 초기화한다.
- @Override public void printInfo()
- printInfo() 메서드를 오버라이딩하여 일반주 종목 정보를 출력.
PreferredStock 클래스
- PreferredStock extends Asset implements Valuable
Asset을 상속,Valuable을 구현.
- private double dividendRate;
- 추가로
dividendRate(배당률)라는 필드를 가짐.
- 추가로
- public PreferredStock(String name, double price, double dividendRate) {super(name, price); this.dividendRate = dividendRate; }
- 생성자를 통해
name,price,dividendRate를 초기화.
- 생성자를 통해
- @Override public void printInfo()
printInfo()를 오버라이딩하여 우선주의 정보(배당률 포함)를 출력하기.
#
#3 의문점
Asset, Valuable의 Stock으로의 흐름과 Asset, Valuable의 PreferredStock으로의 흐름?
- 추상 클래스 Asset
- “모든 자산이라면 name과 price를 가져야 하며, 자신을 소개하는 방법인 printInfo() 메서드를 반드시 가져야 한다”라는 기본 골격을 생성하고 printInfo()를 선언만 해둔다.
- 인터페이스 Valuable
- “가치 있는 자산이라면 반드시 printInfo()를 구현해야 한다”라는 규약을 정의하고, 추가로 updatePrice(double price)라는 기본 기능을 메뉴얼에 적어둔다.
- Stock 클래스
- Asset을 상속받아서 name과 price 필드를 물려받음
- printInfo()를 구현하면서 “나는 일반주이고, 종목명은 name, 현재가는 price원이다”라는 구체적인 출력 내용을 정의
- 동시에 Valuable 인터페이스를 구현
- 규약을 확인해보니 printInfo()는 이미 Asset에서 추상 메서드로 선언되어 있었고, Stock이 그것을 구체적으로 작성했으므로 인터페이스 규약을 만족
- Valuable 인터페이스를 구현했으므로 printInfo()와 updatePrice(double price) 메서드를 사용할 수 있음
- 결국 Asset에서 내려온 골격(name, price, printInfo())과 Valuable에서 정한 규칙 및 기능(printInfo(), updatePrice(double price))이 Stock 클래스 안에서 결합됨
- PreferredStock 클래스
- Asset을 상속받아 기본 필드인 name과 price를 물려받고, printInfo를 구현
- 일반주와는 다르게 배당률이라는 고유한 특징이 있으므로 새로운 필드 dividendRate를 추가.
- printInfo에서는 이름, 가격과 함께 배당률도 출력.
- 결론
- Asset이 제공하는 공통 골격(name, price, printInfo())과 Valuable이 정한 규약(printInfo()) 및 기능(updatePrice(double price))이 Stock과 PreferredStock에 각각 결합되어 Stock은 일반주로서 name과 price를 출력하고 PreferredStock은 여기에 dividendRate를 더해 고유 특성을 반영한다.
#
3. 다형성 #
#1 개념 및 목적
- 개념
- 객체지향 프로그래밍에서 하나의 타입으로 여러 형태의 동작을 표현 즉 같은 이름의 메서드를 호출하더라도 객체의 실제 타입에 따라 실행되는 동작이 달라지는 특성
- 이를 가능하게 하는 조건은 상속과 메서드 오버라이딩으로 구현되고 보통 업캐스팅과 함께 활용된다.
- 부모 클래스 타입의 참조 변수를 통해 메서드를 호출하면, 실행 시점에는 실제 객체 타입에 맞는 오버라이딩된 메서드가 실행된다.
- 목적
- 코드의 유연성 확보: 하나의 부모 타입으로 여러 자식 객체를 다룰 수 있기 때문에, 코드 구조를 단순하게 유지하면서 다양한 객체를 일관된 방식으로 처리할 수 있어서 새로운 자식 클래스가 추가되더라도 기존 코드를 거의 수정하지 않고 확장이 가능하다.
#
#2 샘플 코드
// 부모 클래스
class Stock {
protected String name;
protected double price;
public Stock(String name, double price) {
this.name = name;
this.price = price;
}
// 부모 메서드
public void printInfo() {
System.out.println("[일반주] 종목: " + name + ", 가격: " + price + "원");
}
}
// 자식 클래스
class PreferredStock extends Stock {
double dividendRate;
public PreferredStock(String name, double price, double dividendRate) {
super(name, price);
this.dividendRate = dividendRate;
}
// 부모 메서드를 오버라이딩
@Override
public void printInfo() {
System.out.println("[우선주] 종목: " + name + ", 가격: " + price + "원, 배당률: " + dividendRate + "%");
}
// 자식 클래스만 가진 메서드
public void showDividend() {
System.out.println("배당률은 " + dividendRate + "% 입니다.");
}
}
// 실행 클래스
public class PolymorphismExample {
public static void main(String[] args) {
// 업캐스팅 (자식 → 부모 타입)
Stock stock = new PreferredStock("스칼라 AI", 17500, 5.0);
// 부모 타입으로 참조했지만, 실제 실행은 자식 클래스의 메서드가 호출됨
stock.printInfo();
// 다운캐스팅 (부모 타입 → 자식 타입)
if (stock instanceof PreferredStock) {
PreferredStock ps = (PreferredStock) stock;
ps.showDividend(); // 자식 클래스 고유 메서드 사용 가능
}
}
}
Stock
- class Stock
- 주식 개념 부모 클래스
- protected String name; protected double price;
- 주식의 이름과 가격을 저장하는 필드(멤버 변수)
- protected
- 같은 패키지 내부나 상속받은 자식 클래스에서 접근 가능하다. 외부에서는 직접 접근 불가하다.
public Stock(String name, double price)- 생성자(Constructor)
name과price를 받아 초기화
public void printInfo()- 주식 정보를 출력
- System.out.println("[일반주] 종목: " + name + “, 가격: " + price + “원”);
- “일반주”라고 표시하고 종목명과 가격을 보여준다
- 자식 클래스에서 오버라이딩 대상인 메서드
PreferredStock
- class PreferredStock extends Stock
- Stock을 상속받은 자식 클래스.
- 상속을 통해 name과 price를 물려받았는데 배당률(dividendRate)이라는 속성을 추가하여 “우선주”를 구체화함.
- super(name, price);
- 부모 클래스의 생성자를 호출
- @Override public void printInfo()
- 부모 클래스 printInfo()를 오버라이딩
- 실행 시점에는 동적 바인딩에 의해, 객체의 실제 타입이 PreferredStock이면 이 메서드가 실행된다.
- public void showDividend()
- 자식 클래스에만 있는 메서드. 배당률을 따로 출력하는 기능.
- 부모 타입 변수로는 접근할 수 없고, 자식 타입으로 다운캐스팅해야 호출할 수 있다.
#
#3 의문점
“@Override public void printInfo()를 오버라이딩하면 실행 시점에 객체의 실제 타입에 맞는 메서드가 호출된다”의 의미?
- @Override public void printInfo()를 오버라이딩?
- 부모 Stock에는 printInfo()가 있는데 자식 PreferredStock이 똑같은 메서드 시그니처(메서드 이름, 매개변수 목록, 반환형이 동일)로 다시 정의하면 그게 오버라이딩.
- printInfo() 실행 시점에 객체의 실제 타입에 맞는 메서드가 호출된다?
- printInfo()같은 인스턴스 메서드는 2단계로 처리되는데
- 메서드 호출
- 컴파일러는? 변수의 선언 타입을 보고 “이 메서드를 불러도 되는지” 확인한다.
- Stock s = new PreferredStock(…) 일때 s.printInfo(); 하면 s가 Stock 타입이니까, Stock 클래스에 printInfo()가 있는지만 확인한다.
- 실제 구현
- JVM은? 실제 객체가 누구인지 확인하는데
- 지금 s가 참조하는 건 Stock이 아니라 PreferredStock 객체니까 “PreferredStock에 printInfo()가 오버라이딩돼 있네? 그럼 이걸 실행해야겠다.” 하고 결정한다.
- 메서드 호출
- printInfo()같은 인스턴스 메서드는 2단계로 처리되는데
- 결론
- printInfo() 호출하면 컴파일러는 변수선언을 보고 s가 Stock 타입이고 Stock 안에 printInfo() 있으니까 호출 승인하고, 어떤 버전의 printInfo()가 실행될지는 아직 정해지지 않았고, JVM이 객체를 확인했을때 Stock객체라면 부모 클래스 버전
printInfo()이 실행, PreferredStock이라면 그 클래스에서 정의된printInfo()를 실행한다.
- printInfo() 호출하면 컴파일러는 변수선언을 보고 s가 Stock 타입이고 Stock 안에 printInfo() 있으니까 호출 승인하고, 어떤 버전의 printInfo()가 실행될지는 아직 정해지지 않았고, JVM이 객체를 확인했을때 Stock객체라면 부모 클래스 버전
#
동적 바인딩?
- 실행할 때 객체의 실제 타입을 보고 그에 맞는 메서드를 선택하는게 동적 바인딩. (s라는 변수가 Stock 타입으로 선언되어 있어도, new PreferredStock(…)로 만든 객체를 가리키고 있다면 자식 쪽에 오버라이딩된 메서드가 실행됨)
#
4. 상속 #
1. 개념 및 목적
- 개념
- 기존(부모) 클래스가 가진 속성과 메서드를 새로운(자식) 클래스가 계승하여 활용할 수 있도록 하는 개념
- 자식 클래스는 부모 클래스가 정의한 필드와 메서드를 직접 사용할 수 있다.
- 필요에 따라 새로운 속성과 기능을 추가하거나, 부모 메서드를 오버라이딩(Overriding)하여 구체적인 동작을 재정의할 수 있고
- 이를 통해 자식 클래스는 부모 클래스가 제공하는 공통 기능을 기반으로 기본 구조와 일관성을 유지하면서도, 고유한 특성과 요구 사항을 반영하여 더욱 구체적이고 특화된 클래스로 확장될 수 있다.
- 목적
- 코드 재사용성: 부모 클래스에 정의된 공통 속성과 기능을 여러 자식 클래스에서 공유할 수 있어, 중복 코드를 줄이고 전체 코드 구조를 간결하게 만든다.
- 유지보수성과 확장성: 공통 로직은 부모 클래스에만 수정하면 되고, 자식 클래스는 필요에 따라 기능을 덧붙이거나 오버라이딩을 통해 동작을 변경할 수 있어 유지보수가 쉽고 새로운 기능 추가도 용이하다.
- 다형성 기반 마련: 부모 타입으로 자식 객체를 다룰 수 있고, 실행 시점에는 실제 객체의 타입에 맞는 동작이 수행되므로 유연한 구조를 만들 수 있다.
#
2. 샘플 코드
// 부모 클래스
class Stock {
protected String name;
protected double price;
public Stock(String name, double price) {
this.name = name;
this.price = price;
}
// 부모 메서드
public void printInfo() {
System.out.println("[일반주] 종목: " + name + ", 가격: " + price + "원");
}
}
// 자식 클래스
class PreferredStock extends Stock {
double dividendRate;
public PreferredStock(String name, double price, double dividendRate) {
super(name, price);
this.dividendRate = dividendRate;
}
// 부모 메서드를 오버라이딩
@Override
public void printInfo() {
System.out.println("[우선주] 종목: " + name + ", 가격: " + price + "원, 배당률: " + dividendRate + "%");
}
// 자식 클래스만 가진 메서드
public void showDividend() {
System.out.println("배당률은 " + dividendRate + "% 입니다.");
}
}
// 실행 클래스
public class PolymorphismExample {
public static void main(String[] args) {
// 업캐스팅 (자식 → 부모 타입)
Stock stock = new PreferredStock("스칼라 AI", 17500, 5.0);
// 부모 타입으로 참조했지만, 실제 실행은 자식 클래스의 메서드가 호출됨
stock.printInfo();
// 다운캐스팅 (부모 타입 → 자식 타입)
if (stock instanceof PreferredStock) {
PreferredStock ps = (PreferredStock) stock;
ps.showDividend(); // 자식 클래스 고유 메서드 사용 가능
}
}
}
부모 클래스 Stock
- class Stock
- 주식 개념 부모 클래스
- protected String name; protected double price;
- 주식의 이름과 가격을 저장하는 필드(멤버 변수)
- protected
- 같은 패키지 내부나 상속받은 자식 클래스에서 접근 가능하다. 외부에서는 직접 접근 불가하다.
public Stock(String name, double price)- 생성자(Constructor)
name과price를 받아 초기화
public void printInfo()- 주식 정보를 출력
- System.out.println("[일반주] 종목: " + name + “, 가격: " + price + “원”);
- “일반주”라고 표시하고 종목명과 가격을 보여준다
- 자식 클래스에서 오버라이딩 대상인 메서드
자식 클래스 PreferredStock
- class PreferredStock extends Stock
- Stock을 상속받은 자식 클래스.
- 상속을 통해 name과 price를 물려받았는데 배당률(dividendRate)이라는 속성을 추가하여 “우선주”를 구체화함.
- super(name, price);
- 부모 클래스의 생성자를 호출
- @Override public void printInfo()
- 부모 클래스 printInfo()를 오버라이딩
- 실행 시점에는 동적 바인딩에 의해, 객체의 실제 타입이 PreferredStock이면 이 메서드가 실행된다.
- public void showDividend()
- 자식 클래스에만 있는 메서드. 배당률을 따로 출력하는 기능.
- 부모 타입 변수로는 접근할 수 없고, 자식 타입으로 다운캐스팅해야 호출할 수 있다.
#
#3 의문점
그래서 업캐스팅과 다운캐스팅이 어떻게적용되는가?
- 업캐스팅
- 자식 객체를 부모 타입 변수에 담는것
- PreferredStock이 Stock을 상속받는 상황에서 Stock stock = new PreferredStock(…);처럼 쓰면 실제 객체는 PreferredStock이지만 참조 변수의 타입을 Stock으로 지정했기 때문에 컴파일러는 이 객체를 부모 클래스 객체 형식으로 인지한다.
- 실행 시점에 stock.printInfo()를 호출하면 실제 객체가 PreferredStock이므로 자식이 오버라이딩한 메서드가 실행된다. 핵심은 부모객체처럼 인지되면서도 실제동작은 자식클래스의 성질이 반영된다.
- 업캐스팅 하는이유?
- 여러 종류의 자식 클래스를 일괄적으로 묶어서 처리할수있기때문에 코드가 단순해진다.
- 다운캐스팅
- 부모 타입 변수에 들어 있는 객체를 다시 자식 타입 변수로 변환하는것.
- Stock stock이라는 부모 타입 참조가 있지만, 실제 객체가 PreferredStock이라면 (PreferredStock) stock으로 형변환을 거치면 자식 타입 변수로 다룰수있다 즉 자식만이 가진 고유한 메서드 showDividend() 를 호출할수있다.
- 결론
- 상속 구조에서는 같은 객체를 필요에 따라 업캐스팅 ↔ 다운캐스팅으로 부모 클래스 ↔ 자식 클래스로 바꿔 다루면서 공통성과 특수성을 효율적으로 반영할수있다.
#
5. 공통 특성: 인터페이스와 구현의 분리 #
- 캡슐화, 추상화, 다형성, 상속은 결국 인터페이스와 구현이 분리되는걸 활용하는게 포인트인것같은데 인터페이스와 구현의 분리가 각각 어떻게 활용되었는가?
- 캡슐화 - 데이터(구현)를 숨기고 메서드(인터페이스)만 공개
- 추상화 - “무엇을 할 수 있는가”(인터페이스)와 “어떻게 할 것인가”(구현)를 분리
- 다형성 - 부모의 틀(인터페이스)은 유지하면서, 자식에서 구체 구현을 다양하게 정의
- 상속 - 호출하는 쪽은 부모 타입(인터페이스)만 보고, 실행되는 쪽은 실제 객체의 구현을 따른다.