[타입 호환성]
: 대부분의 언어에서 매 맥락마다 타입 동등성을 요구하지 않는다.
: 대신, 맥락이 타입 호환성이 유지되는 정도면 충분하다고 말한다.
# 타입 호환성 유지 확인 케이스
1) Assignment statement
: 오른쪽의 타입이 왼쪽의 타입과 호환 되어야 한다. ex) int a = b; // b는 int 타입인 a와 호환되는 타입여부
2) operands of '+' 타입
: '+', 즉 더하기를 지원하는 일반적인 타입들이여야 사용 가능하다. ex) a + b; // a, b는 더하기 연산 가능한 타입
3) subroutine 호출
: subroutine에 전달되는 타입들은 선언시 사용한 파라미터 타입들과 호환 되어야 한다.
[언어별 타입 호환성]
: 언어마다 타입 호환성의 정의 범위가 다르다.
# 언어별 타입 호환성 예시
int a = 3.2' 2 + 3.2; |
: 이와 같은 선언은 언어마다 호환성 범위에 따라 허용 범위가 달라진다.
: JAVA와 Ada에서는 이와 같이 사용 할 수 없다. (Ada : 타입 S와 T가 동등성이고, 서로 subtype인등 같은 종류여야지만 가능)
[자동 형 변환(Coercion)]
: 한 값의 타입을 다른 타입으로 자동으로, 암시적으로 전환(conversion)해주는 특성이다.
: 실행 시점에 수행되는 동적 시맨틱 확인 코드 필요
: 실행 시점에 수행되는 low-level representation 간 변환 코드 필요
# C에서의 자동 형 변환
: 비교적 weak 타입 시스템이라, 약간의 자동형변환만 수행한다.
short in s; // 16비트
unsigned long int l; // 32비트
char c; // 8비트
float f; // 32비트
double d; // 64비트
// 1) l의 아래 bit들이 signed number로 변환
s = l;
// 2) 추가된 위 bit를 0으로 채우고, 기존의 signed bit(맨위 부호 bit)를 숫자로 취급한다
l = s;
// 3) short <- char
s = c;
// 포맷 변경. 숫자 손실 발생가능 (하지만 동적 시맨틱 오류 발생은 X)
f = d;
: 결론적으로 C언어는 큰 범위의 타입에서 작은 범위의 타입으로 형변환 중 일어나는 포맷손실에 대해 따로 작업 하지 않는다 ( = 동적 시맨틱 오류 검사 X )
: 설계자는 어디까지 형 변환 허용해줄지 판단 하는 것이 중요 하다.
[Universal Reference Types]
: 어떤 타입이든 저장 할 수 있는 보편적 목적의 컨테이너 타입이 존재 한다.
# 각 언어에서의 universal reference type
1) C/C++ : void* (void 포인터) 2) Java, C# : Object |
: 그렇다고 이 타입으로 할당될 수는 없지만 타입의 안전성을 고려하지 않은 것이다.
: 객체의 타입이 universal reference에 의해 참조됨을 알수 없기에, 컴파일러는 해당 객체에 대해 어떤 연산자도 수행되는 걸 허용하지 않는다.
: 특정 참조 타입의 객체에 재할당되는 것은 좀 까다로워, 타입 safety가 필요하다.
# 특정 참조 타입의 객체에 재할당
float f = value; void* p = &f; int* p = p // 오류 발생(객체의 비트해석 부정확히 수행) |
: 객체지향 언어에서는, 위와 같은 문제되는 상황들에 대해 유효성을 확인해준다.
# 객체지향 언어에서 왼쪽의 객체가 오른쪽의 객체의 할당을 지원 유효성 검사
Object o = new B(); A a = (A)o; |
: 위와 같이 객체가 self-descriptive(자기-서술)하게 만들어, 포함될 타입의 tag를 기술해 확인할 수 있게 해준다.
: Java와 C#과 같은 객체지향 언어에서는 universal이 특정 할당되려면 type cast가 필요하고(위),
해당 universal가 cast된 타입에 참조될 수 없으면 exception을 발생 시킨다. ( ClassCastException )
# Java에서 ClassCastException 발생
A a = new A(); Object o = new String("hello"); a = (A) o; // ClassCastException 발생 |
# C++에서의 dynamic_cast 사용
int main() { A* a; A* b = new B; // B는 A의 상속관계, C는 독립관계지만 우연히 같은 메소드 보유 C* c = new C; a = dynamic_cast<A*>(c); // 형변환. (A*)(c)와 다른 개념 ( = static_cast(강제)) print(a); // null, 잘못된 형변환일때 포인터에 null 값들이 들어가기에 잘못된 형변환 확인 가능 a = dynamic_cast<A*>(b); print(a); delete c; delete b; } |
: 결론은 tagging 방식이용해서 dynamic_cast 가능
# universal reference 클래스 정리와 Generic**
: 초기 버전의 Java와 C#에서, 프로그래머는 최상위 클래스의 객체를 위한 컨테이너 클래스인 Object or object를 가진다.
: 하지만 이러한 Object를 이용하는 방법(위에서 쭉 본 방법)은 넣을땐 상관 업지만, 뺄때 반드시 type cast 가 필요하다.
: 심지어 이런 type cast를 해줘도 잘못된 cast 발생시 exception 처리 까지 해줘야 한다.
: 이런 문제는 넣을때 어떤 종류든 해당 공간에 넣을 수 있기에 발생하는 문제이다 ( safety 문제 )
: 그래서 오늘날에 Generic 이라는 개념이 존재하고 사용되어, 기존 obejct 방식은 사용이 감소하였다.
: 이 Generic 방법은 애초에 넣을때 기술된 타입의 클래스만 넣을 수 있게 하여 보다 안정적인 방법이다.
// 기존 object 방법 ArrayList a = new ArrayList(); a.add(value) // 어떤 종류의 타입이든 value에 들어갈 수 있다. s = (String) a.pop() // 타입 cast 필요.. 실행 시점에 string 아닌 타입들에대해선 exception 발생
// Generic 방법 ArrayList<String> a = new ArrayList<String>(); // 애초에 String 타입만 해당 배열에 들어갈 수 있게 허용 a.add(value) s = a.pop()
|
: 특히 타입 tag 없는 언어에선(cast), universal reference 타입의 객체가 실행 시점까지 그 타입들을 식별 할 수 없다.
'[프로그래밍 언어론]' 카테고리의 다른 글
프로그래밍 언어론 7-3강. 파리미터의 다형성 (Parametric Polymorphism) (0) | 2019.12.15 |
---|---|
프로그래밍 언어론 7-2-3강. 타입 추론(Type Inference) (0) | 2019.12.14 |
프로그래밍 언어론 7-2-1강. 타입 동등성(Type Equivalence) (0) | 2019.12.13 |
프로그래밍 언어론 7-2강. 타입 검사(Type Checking) (0) | 2019.12.13 |
프로그래밍 언어론 7-1-4강. 타입의 분류(Classification of Types) (0) | 2019.12.12 |