OOP, 제네릭과 타입 소거
OOP
클래스와 객체의 차이
클래스는 객체들의 분류, 집합, 설계도(blueprint)이다.
반면에 객체는 클래스의 인스턴스이며, 런타임에 생성된다.
객체 지향에서 객체들의 공통적 특성을 모아 추상화한 결과가 클래스이다.
OOP의 주요 개념
캡슐화
캡슐화는 접근제어자(public
, protected
, default
, private
)를 이용해 객체의 정보를 은닉하는 것을 말한다.
객체의 내부 데이터와 내부 구현을 외부 컴포넌트로부터 잘 숨기면, API를 통해서만 다른 컴포넌트와 소통할 수 있기 때문에 내부 구현 방식이 바뀌어도 외부 컴포넌트는 영향을 받지 않는다. 또한 컴포넌트 각각을 독립적으로 테스트할 수 있다.
상속
상위 클래스와 하위 클래스가 존재하며, 하위 클래스로 갈수록 특성이 추가되며 확장된다.
하위 클래스는 상위 클래스의 필드와 메서드를 상속받으며, 하위 클래스는 상위 클래스로 표현될 수 있어야한다.
다중 상속은 불가능하다.
추상화
추상화란 여러 개념들의 공통적인 특성이나 속성등을 추출하여 파악하는 것을 말한다.
즉, 구체적 개념들을 분해하여 관심 있는 특성들을 모아 재조합하는 것이다.
객체지향의 추상화는 곧 모델링이다. 클래스를 설계할 때, 객체들의 공통적인 특성들 중 보여주고 싶은 주요 특성들을 모아 필드와 메서드로 만든다.
다형성
다형성은 사용의 편의성을 위해 오버로딩과 오버라이딩을 지원하는 것을 말한다.
- 오버로딩은 같은 메서드 이름으로 다른 인자 목록을 지원하여 메서드를 중복 정의하는 것이다.
- 오버라이딩은 같은 메서드와 같은 인자 목록으로 하위 클래스에서 상위 클래스의 메서드를 재정의하는 것이다.
객체지향이란?
시스템을 상호작용하는 자율적인 객체들의 공동체로 바라보고, 객체를 이용해 시스템을 분할하는 방법
상태를 캡슐화한 자율적인 객체가 서로 협력하며 시스템을 이룬다.
SOLID 원칙 각각을 설명하고 이를 설계에 어떻게 적용할 수 있는가?
SRP (단일 책임 원칙)
어떤 클래스를 변경해야 하는 이유는 오직 하나뿐이어야 한다. - 로버트 C.마틴
- 설계 : 클래스를 각각의 역할과 책임에 따라 분리한다.
- 검증
- 하나의 클래스가 너무 많은 역할과 책임을 가지고 있는가?
- if문이 반복적으로 사용되는가?
- 객체의 멤버(공개 메서드, 필드, 상수)가 단일 책임에 의해서만 변경되는가?
OCP (개방 폐쇄 원칙)
- 의미 : 확장에는 열려 있고, 수정에는 닫혀 있어야 한다.
- 설계 : 인터페이스를 활용하면 추상화된 인터페이스를 의존하고 구체 구현체만 변경할 수 있다.
- 검증
- 기존 코드의 변경 없이, 시스템의 기능을 확장할 수 있는가?
LSP (리스코프 치환 원칙)
서브 타입은 언제나 자신의 기반 타입(base type)으로 교체할 수 있어야 한다. - 로버트 C.마틴
- 설계 : 하위 클래스가 부모 클래스의 행위를 변경하지 않으며 상속한다.
- 검증
- 하위 타입의 객체를 상위 타입의 객체로 변경할 수 있는가?
ISP (인터페이스 분리 원칙)
- 의미 : 인터페이스를 작게 분리하라.
- 클라이언트는 자신이 사용하지 않는 인터페이스에 의존하면 안된다.
- 설계: 인터페이스를 잘게 쪼갠다.
- 검증
- 클라이언트가 자신이 사용하지 않는 인터페이스에 의존하는가?
DIP (의존 역전 원칙)
- 의미 : 구체적인 것이 추상화된 것에 의존해야 한다.
- 설계 : 구체 클래스가 아닌 인터페이스, 추상 클래스에 의존하게 함으로써 구현체가 변경되어도 사용 코드에 영향을 받지 않는다.
- 검증
- 자주 변경되는 구체 클래스에 의존하는가?
제네릭
제네릭이 없던 시절의 문제점과 이를 해결하기 위한 설계적 의도
제네릭이 필요한 이유
제네릭은 java 5부터 지원되기 시작하였다.
제네릭이 없던 시절에는, 동일한 로직이 수행되더라도 다른 타입이라면 메서드를 새롭게 정의해주어야 했다.
만약 여러 타입을 지원하기 위해 Object
으로 선언한다고 해도, 객체를 꺼낼때마다 형변환을 해주어야하며 잘못된 타입이 들어오면 캐스팅 예외가 발생하였다.
ex) 제네릭 이전 Collection
구현
제네릭 등장 이후
제네릭을 사용하여 같은 로직을 다양한 타입에 적용할 수 있게 되었다.
또한 컴파일 시점에 타입 오류를 잡아낼 수 있게 되었으므로 런타임 예외(ex) 캐스팅 예외)의 위험이 줄어들었다.
제네릭은 런타임에 타입이 소거되므로 필요한 곳에 자동으로 형변환 코드가 추가된다.
제네릭이 재사용성과 강력한 타입 검증을 동시에 달성하는 방법
재사용성
위에서 이야기했다시피, 제네릭을 통해 한 메서드가 다양한 타입을 지원할 수 있게 되었다.
불공변성으로 강력한 타입 검증 가능
제네릭은 타입 안전성을 지키기 위해 불공변성을 가진다.
불공변성을 가지면 A가 B의 하위 타입이어도, T<A>
와 T<B>
사이에는 상속 관계가 없다.
제네릭에서, 지정된 타입이 아닐 경우 불공변성 특성으로 인해 컴파일 시점에 예외를 잡을 수 있다.
와일드카드 (반공변성)
만약 제네릭을 이용하면서도 공변성이 필요하다면 어떻게 해야할까?
와일드카드를 이용하면 부분적으로 공변성을 구현할 수 있다(반공변성).
? extends T
는 T와 T의 모든 하위 타입을 허용하며, ? super
는 T와 T의 모든 상위 타입을 허용한다.
하위 호환성 지원 : 타입 소거
제네릭 등장 이전에는 Object
로 모든 타입을 지원하도록 구현되어있었다.
제네릭 등장 이후 이전 코드의 하위 호환성을 지키기 위해, 런타임에는 타입이 소거되도록 구현하였다.
따라서 List<String>
은 List
가 되어 제네릭을 사용하는 코드와 사용하지 않은 코드가 같은 클래스를 참조하게 된다.
'우테코' 카테고리의 다른 글
[Lv1] 레벨1 학습 내용 정리 (3) | 2025.04.14 |
---|---|
[Lv1] 장기 미션 회고 (0) | 2025.04.13 |
[Lv1] 블랙잭 미션 회고 (8) | 2025.04.11 |
[Lv1] 출석부 미션 회고 (1) | 2025.04.08 |
[Lv1] 로또 미션 회고 (1) | 2025.04.08 |