원제: 타입으로 견고하게, 다형성으로 유연하게
- 타입 검사
- 타입: 프로그램에 존재하는 값들을 그 능력에 따라 분류한 것
- 타입 검사기: 프로그램이 타입 오류를 일으키는지 자동으로 판단해 주는 프로그램
- 타입 안정성: 검사기가 통과시켰다면 타입 오류없이 프로그램이 실행가능함이 증명되는 것
- 언어 분류
- 정적 타입 언어: 프로그램이 실행하기 전에 타입 오류를 확인하는 언어
- 타입 검사의 불편함을 감내하는 대신, 오류를 사전에 차단할 수 있음
- 언어 자동완성의 혜택을 최대로 누릴 수 있고, 타입 검사로 인한 실행 중 오버헤드가 줄어들음
- 타입 추론: 정적 언어에서 타입을 매번 표시해야 하는 불편함을 감소시키고 생산성을 향상시킴
- 동적 타입 언어: 프로그램 실행 중에 타입 오류를 확인하는 언어
- 유연하고 빠른 컴파일 성능을 자랑하지만 타입 검사로 인한 오류를 잡을 수 없음
- 서브타입에 의한 다형성
- 서브타입:
A는 B이다라는 타입 사이의 관계 ↔︎ 슈퍼타입
- 방식
- 이름에 의한 서브타입(nominal subtyping)
- 타입이 보여주는 클래스 이름과 클래스 사이의 상속 표기만을 고려하는 타입
- ex) 자바, C++ 등의 상속 관계 표기법
- 구조에 의한 서브타입(structural subtyping)
- 클래스의 메서드와 필드에 따라 서브타입으로 고려하는 타입
- ex) javascript, go 등의 덕 타이핑
- 추상 클래스(interface, trait 등으로도 불림)
- 메서드 시그니처: 함수의 이름, 매개변수 타입, 결과 타입의 묶음
- 추상 메서드: 메서드 시그니처만 존재하는 메서드
- 타입 검사기에 메서드의 존재 정보를 제공하는 것이 목적
- 집합론적 타입
- 최대 타입(top type): 아무 값이나 가능하다는 사실을 표현하는 가장 큰 타입(Any)
- 최소 타입(bottom type): 어떤 값도 속하지 않는 타입으로 모든 타입의 서브 타입(Nothing)
- 이거나 타입(union type):
A이거나 B 를 표현하는 타입
- 사용할 때 프로그램이 단순해서 타입 검사가 통과하기 쉬워진다
- 이면서 타입(intersection type):
A이면서 B 를 표현하는 타입
- 함수와 서브 타입: A가 B의 서브타입이고, C가 D의 서브타입이라면 함수 B=>C는 A=>D의 서브 타입이다
- 즉 함수형일때는 서브타입 관계가 인자는 반대로 뒤집히고, 리턴형은 그대로 유지된다
- 하기 기술(가변성 파트) 하겠지만 매개변수는 반변성이고, 결과 타입은 공변성이란 뜻
- 타입 매개변수(제네릭)에 의한 다형성
- 제네릭 함수: 한 개 이상의 타입 매개변수를 가지는 함수
- 인자 값이 함수 안에서 특정 능력이 필요하다면 제네릭 함수가 될 필요가 없다
- 제네릭 타입: 타입에 타입 매개변수가 추가된 타입
- 제네릭 클래스: 타입 매개변수를 가진 클래스
- 심화( 스칼라, 오캐멀에만 존재 )
- 무엇이든 타입: 타입 매개변수를 가진 함수 어떤 것이든 받아들이는 타입
- 제네릭 메서드를 가진 객체를 인자로 넘기기 보다 제네릭 함수를 바로 인자로 넘기는 것이 간결하기 때문에 유용하다
- 무엇인가 타입: 타입을 가려 어떤 행위를 할 수 없지만 타입을 인자, 반환으로는 쓸 수 있는 타입
- 프로그램이 상당히 복잡하거나 라이브러리 작성 시 유용하다
- 제네릭 + 서브타입에 의한 다형성
- 제네릭 클래스도 상속에 따른 서브타입이 존재한다
- 서브타입 관계는 매개변수를 따라간다 → ex) <Int>, <String> 은 다른 서브 타입
- 매개변수 제한
- 매개변수가 될 수 있는 타입을 제한하기 위한 방법
- 상한(Upper Bound): 최대 타입을 제한하기 위해 지정 → 모든 T는 A의 서브타입
- 하한(Lower Bound): 특정 타입의 슈퍼타입만으로 제한하기 위해 지정 → 모든 T는 A의 슈퍼타입
- 재귀적 타입 매개변수 제한: T의 상한에 T 자신이 이용되는 경우
- ex) <T : Comparable<T>> 에서 T는 자신 자신을 Comparable<T>로 제한한다
- 가변성(Variance)
- 제네릭 타입과 타입 인자 사이의 관계
- 제네릭 타입 사이의 서브타입 관계를 추가로 정의하는 기능
- 하나의 제네릭 타입에서 타입 인자만 다르게 하여 생기는 타입들 사이의 서브 타입 관계
- 종류 ( B는 A의 서브타입으로 가정한다 )
- 공변(Covariance) = out
- 제네릭 타입이 타입 인자와 함께 변하는 가변성
- ex) List<A> 에서 List<B> 로 변할 때 제네릭 타입 역시 서브타입으로 변하는 관계
- 불변(invariance)
- 제네릭 타입이 타입 인자와 무관한 가변성
- ex) List<A> 에서 List<B> 로 변할 때 제네릭 타입은 서로 상속 관계가 아닌 관계
- 반변(contravariance) = in
- 제네릭 타입이 타입 인자의 서브 타입 관계를 뒤집는 가변성
- ex) List<A> 에서 List<B> 로 변할 때 제네릭 타입이 List<B> → List<A> 로 뒤집히는 관계
- 즉, T를 출력에만 사용하면 공변, 입력에서만 사용하면 반변, 둘 다 쓴다면 불변이다
- 사용할 때(메서드) 가변성 지정하기
- 제네릭 타입을 자유롭게 정의 할 수 있어 제네릭 클래스보다 유용하다
- 다만 정의할 때(클래스) 가변성이 이미 불변이 아닌 경우에는 사용할 수 없다
- 함수 오버로딩에 의한 다형성 ( ad-hoc polymophism)
- 오버로딩: 매개변수 타입이 다른 같은 이름의 함수를 여러 개 정의하는 개념
- 함수 선택(function dispatch): 언어가 오버로딩된 함수 중 호출할 함수를 자동으로 고르는 것
- 선택 규칙: 매개변수 수와 타입이 맞는 것을 찾는다 → 인자 타입에 가장 특화된 것(정확한 타입)을 찾는다 → 인자의 정적 타입만 고려하여 찾는다 = static dispatch
- 메서드 오버라이딩
- 특화된 동작을 정의하는 가장 좋은 방법
- 반드시 자식 클래스의 결과 타입은 부모 클래스의 메서드 결과 타입의 서브타입이어야 한다
- 선택 규칙
- 기본적으로 함수 선택과 동일한 규칙을 사용한다
- 추가적으로 수신자의 동적 타입을 고려하여 선택한다 ( dynamic dispatch )
- 타입 클래스
- 특정 타입을 위한 함수가 존재한다는 사실을 표현하기 위한 개념
- 즉, T가 특정 타입에 속하기 위해서는 A라는 메서드를 가져야함을 강제한다 → 클래스와 무관
- 라이브러리에 정의된 타입을 다루는데 매우 유용하다
- vs 추상 클래스
- 카인드
- 선형 타입(linear type): 각각이 값이 무엇을 할 수 있만 아니라 언제, 몇 번 할 수 있는지도 표현 가능한 타입