2.7.2 암시적 캐스팅(Implicit Casts)
2.7.2 암시적 캐스팅(Implicit Casts)
클래스와 달리 추상은 암시적 캐스팅을 정의할 수 있어요. 두 종류의 암시적 캐스팅이 있습니다:
- 직접(Direct): 추상 타입을 다른 타입으로(또는 다른 타입에서 추상으로) 직접 캐스팅할 수 있게 해줍니다. 추상 타입에
to와from규칙을 추가해 정의하며, 추상의 기반 타입과 유니파이되는 타입에만 허용됩니다. - 클래스 필드(Class field): 특별한 캐스트 함수 호출을 통한 캐스팅을 허용합니다. 이 함수들은
@:to와@:from메타데이터로 정의되며, 모든 타입에 허용됩니다.
다음 코드 예제는 직접 캐스팅의 예를 보여줘요:
abstract MyAbstract(Int) from Int to Int {
inline function new(i:Int) {
this = i;
}
}
class Main {
static public function main() {
var a:MyAbstract = 12;
var b:Int = a;
}
}
MyAbstract를 from Int 및 to Int로 선언합니다. 이는 Int에서 할당될 수 있고 Int에 할당될 수 있다는 뜻이에요. 9·10행에서 Int 값 12를 MyAbstract 타입 변수 a에 할당하고(from Int 덕분), 그 추상을 Int 타입 변수 b에 다시 할당합니다(to Int 덕분).
클래스 필드 캐스팅은 의미론은 같지만 완전히 다르게 정의됩니다:
abstract MyAbstract(Int) {
inline function new(i:Int) {
this = i;
}
@:from
static public function fromString(s:String) {
return new MyAbstract(Std.parseInt(s));
}
@:to
public function toArray() {
return [this];
}
}
class Main {
static public function main() {
var a:MyAbstract = "3";
var b:Array<Int> = a;
trace(b); // [3]
}
}
@:from을 정적 함수에 추가하면, 그 함수는 인자 타입에서 추상으로의 암시적 캐스트 함수가 됩니다. 이 함수들은 추상 타입의 값을 반환해야 하고, static으로 선언되어야 해요.
마찬가지로 @:to를 함수에 추가하면, 그 함수는 추상에서 반환 타입으로의 암시적 캐스트 함수가 됩니다.
위 예제에서 fromString 메서드는 값 "3"을 MyAbstract 타입 변수 a에 할당하게 하고, toArray 메서드는 그 추상을 Array<Int> 타입 변수 b에 할당할 수 있게 해요.
이런 종류의 캐스팅을 사용하면, 필요할 때 캐스트 함수 호출이 삽입됩니다. JavaScript 출력을 보면 분명해져요:
var a = _ImplicitCastField.MyAbstract_Impl_.fromString("3");
var b = _ImplicitCastField.MyAbstract_Impl_.toArray(a);
캐스트 함수 두 개를 인라인(inlining)하면 더 최적화할 수 있고, 출력은 다음과 같이 바뀝니다:
var a = Std.parseInt("3");
var b = [a];
적어도 하나가 추상인 타입 A를 타입 B에 할당할 때의 선택 알고리즘(selection algorithm) 은 간단합니다:
A가 추상이 아니면 3으로 간다.A가B를 허용하는to변환을 정의하면 6으로 간다.B가 추상이 아니면 5로 간다.B가A를 허용하는from변환을 정의하면 6으로 간다.- 멈춤, 유니피케이션 실패.
- 멈춤, 유니피케이션 성공.
(선택 알고리즘 흐름도)
설계상 암시적 캐스팅은 전이적(transitive)이지 않습니다. 다음 예제가 보여줘요:
abstract A(Int) {
public function new()
this = 0;
@:to public function toB() return new B();
}
abstract B(Int) {
public function new()
this = 0;
@:to public function toC() return new C();
}
abstract C(Int) {
public function new()
this = 0;
}
class Main {
static public function main() {
var a = new A();
var b:B = a; // valid, uses A.toB
var c:C = b; // valid, uses B.toC
var c:C = a; // error, A should be C
}
}
A에서 B, B에서 C로의 개별 캐스팅은 허용되지만, A에서 C로의 전이 캐스팅은 허용되지 않아요. 이는 모호한 캐스트 경로를 피하고 간단한 선택 알고리즘을 유지하기 위함입니다.
출처: Implicit Casts
본문
두 종류의 암시적 캐스팅
추상은 직접 캐스팅(from/to 규칙)과 클래스 필드 캐스팅(@:from/@:to 메서드)을 통해 암시적 캐스팅을 정의할 수 있어요. 직접 캐스팅은 기반 타입과 유니파이되는 타입에만 쓸 수 있고, 클래스 필드 캐스팅은 모든 타입에 쓸 수 있습니다.
@:from과 @:to
@:from을 붙인 정적 함수는 인자 타입에서 추상으로의 암시적 캐스트를 정의해요. 추상 타입을 반환해야 하고static이어야 합니다.@:to를 붙인 함수는 추상에서 반환 타입으로의 암시적 캐스트를 정의합니다.
사용 시점에 캐스트 함수 호출이 삽입되며, 인라인하면 더 최적화된 코드가 생성됩니다.
선택 알고리즘
할당 시 추상 변환을 선택하는 규칙은 간단해요. A의 to 변환, B의 from 변환을 순서대로 확인해 하나라도 성립하면 성공, 아니면 실패입니다.
전이성 없음
암시적 캐스팅은 전이적이지 않아요. A→B와 B→C가 각각 허용되어도 A→C는 허용되지 않습니다. 이는 캐스트 경로의 모호성을 피하고 단순한 알고리즘을 유지하기 위한 설계예요.