Array 클래스 확장

Flash Player 9 이상, Adobe AIR 1.0 이상

Array 클래스는 최종이 아닌 몇 개의 핵심 클래스 중 하나이므로 사용자 고유의 Array 하위 클래스를 만들 수 있습니다. 이 단원에서는 Array 하위 클래스를 만드는 방법에 대한 예제와 이 과정 동안 발생할 수 있는 일부 문제에 대해 살펴봅니다.

앞서 언급했듯이 ActionScript에서 배열의 유형은 정의되어 있지 않지만 특정 데이터 유형의 요소만 허용하는 Array 하위 클래스를 만들 수 있습니다. 다음 단원의 예제에서는 요소를 첫 번째 매개 변수에 지정된 데이터 유형 값으로 제한하는 TypedArray라는 Array 하위 클래스를 정의합니다. TypedArray 클래스는 Array 클래스를 확장하는 방법을 설명하는 예제로만 사용되며 다음과 같은 여러 가지 이유로 실제로 사용하기에는 적합하지 않을 수 있습니다. 첫 번째, 컴파일 타임이 아닌 런타임에 유형 검사가 실행됩니다. 두 번째, TypedArray 메서드에서 불일치가 발생할 경우, 불일치가 무시되고 예외가 발생하지 않습니다. 메서드를 간단히 수정하여 예외가 발생되도록 할 수는 있습니다. 세 번째, 이 클래스에서는 배열에 모든 유형의 값을 삽입할 수 있는 배열 액세스 연산자의 사용을 막을 수 없습니다. 네 번째, 이 코딩 스타일에서는 성능 최적화보다 간결성을 선호합니다.

참고: 여기에서 설명한 방법을 사용하여 유형 배열을 만들 수 있습니다. 그러나 더 나은 방법은 Vector 객체를 사용하는 것입니다. Vector 인스턴스는 진정한 유형 배열로서, Array 클래스나 모든 하위 클래스에 비해 성능 및 그 밖의 면에서 우수합니다. 이 단원의 목적은 Array 하위 클래스를 만드는 방법을 보여 주는 것입니다.

하위 클래스 선언

extends 키워드를 사용하여 클래스가 Array의 하위 클래스임을 나타낼 수 있습니다. Array 하위 클래스는 Array 클래스와 같이 dynamic 특성을 사용해야 합니다. 그렇지 않으면 하위 클래스가 올바르게 작동하지 않습니다.

다음 코드에서는 TypedArray 클래스의 정의를 보여 줍니다. 이 클래스에는 데이터 유형, 생성자 메서드 및 배열에 요소를 추가할 수 있는 네 가지 메서드를 보유할 수 있는 상수가 포함됩니다. 이 예제에서는 각 메서드의 코드가 생략되어 있지만 다음 단원에서 모두 자세히 살펴봅니다.

public dynamic class TypedArray extends Array 
{ 
    private const dataType:Class; 
 
    public function TypedArray(...args) {} 
     
    AS3 override function concat(...args):Array {} 
     
    AS3 override function push(...args):uint {} 
     
    AS3 override function splice(...args) {} 
     
    AS3 override function unshift(...args):uint {} 
}

이 예제에서는 컴파일러 옵션 -as3 true 로 설정되어 있고 컴파일러 옵션 -es false 로 설정되어 있다고 가정하므로 네 가지 대체 메서드에서는 모두 public 특성이 아닌 AS3 네임스페이스를 사용합니다. 이는 Adobe Flash Builder 및 AdobeFlashProfessional의 기본 설정입니다.

프로토타입 상속 사용을 선호하는 고급 개발자일 경우 TypedArray 클래스에서 두 가지 사항을 약간 변경하여 컴파일러 옵션 -es true 로 설정하여 컴파일할 수 있습니다. 첫 번째, override 특성의 모든 항목을 제거하고 AS3 네임스페이스를 public 특성으로 바꿉니다. 두 번째, 네 가지 super 항목을 모두 Array.prototype 으로 교체합니다.

TypedArray 생성자

생성자에서 임의의 길이의 인수 목록을 허용해야 하므로 하위 클래스 생성자에서 이와 관련한 문제가 발생할 수 있습니다. 문제는 배열을 만들기 위해 superconstructor로 인수를 전달하는 방법입니다. 인수 목록을 배열로 전달할 경우 superconstructor는 이것을 Array 유형의 단일 인수로 생각하여 결과 배열은 항상 1 요소 길이가 됩니다. 인수 목록 전달을 처리하는 기존의 방법은 Function.apply() 메서드를 사용하는 것이며, 이 메서드는 인수 배열을 두 번째 매개 변수로 취하지만 함수 실행 시에는 이것을 인수 목록으로 변환합니다. 그러나 Function.apply() 메서드는 생성자와 함께 사용할 수 없습니다.

남은 유일한 옵션은 TypedArray 생성자에서 Array 생성자의 논리를 다시 만드는 것입니다. 다음 코드는 Array 클래스 생성자에서 사용되는 알고리즘을 보여 줍니다. 이 알고리즘은 Array 하위 클래스 생성자에서 재사용할 수 있습니다.

public dynamic class Array 
{ 
    public function Array(...args) 
    { 
        var n:uint = args.length 
        if (n == 1 && (args[0] is Number)) 
        { 
            var dlen:Number = args[0]; 
            var ulen:uint = dlen; 
            if (ulen != dlen) 
            { 
                throw new RangeError("Array index is not a 32-bit unsigned integer ("+dlen+")"); 
            } 
            length = ulen; 
        } 
        else 
        { 
            length = n; 
            for (var i:int=0; i < n; i++) 
            { 
                this[i] = args[i]  
            } 
        } 
    } 
}

TypedArray 생성자는 다음과 같은 네 가지 변경 사항을 제외하고는 Array 생성자의 코드 대부분을 공유합니다. 첫 번째, 매개 변수 목록에 배열 데이터 유형의 지정을 허용하는 Class 유형의 새로운 필수 매개 변수가 포함됩니다. 두 번째, 생성자로 전달되는 데이터 유형이 dataType 변수에 할당됩니다. 세 번째, else 문에서 length 속성 값이 for 루프 뒤에 할당되어 length 에는 올바른 유형의 인수만 포함됩니다. 네 번째, for 루프 본문은 push() 메서드의 대체 버전을 사용하므로 올바른 데이터 유형의 인수만 배열에 추가됩니다. 다음 예제에서는 TypedArray 생성자 함수를 보여 줍니다.

public dynamic class TypedArray extends Array 
{ 
    private var dataType:Class; 
    public function TypedArray(typeParam:Class, ...args) 
    { 
        dataType = typeParam; 
        var n:uint = args.length 
        if (n == 1 && (args[0] is Number)) 
        { 
            var dlen:Number = args[0]; 
            var ulen:uint = dlen 
            if (ulen != dlen) 
            { 
                throw new RangeError("Array index is not a 32-bit unsigned integer ("+dlen+")") 
            } 
            length = ulen; 
        } 
        else 
        { 
            for (var i:int=0; i < n; i++) 
            { 
                // type check done in push()  
                this.push(args[i]) 
            } 
            length = this.length; 
        } 
    } 
}

TypedArray 대체 메서드

TypedArray 클래스는 배열에 요소를 추가할 수 있는 네 가지 Array 클래스 메서드를 대체합니다. 각각의 경우 대체 메서드는 정확하지 않은 데이터 유형이 요소가 추가되는 것을 막기 위한 유형 검사를 추가합니다. 이렇게 하면 각 메서드가 해당 수퍼 클래스 버전을 호출합니다.

push() 메서드는 for..in 루프를 사용하여 인수 목록을 반복하며 각 인수에서 유형 검사를 수행합니다. 정확하지 않은 유형의 인수는 모두 splice() 메서드를 사용하여 args 배열에서 제거됩니다. for..in 루프가 종료되면 args 배열에는 dataType 유형 값만 포함됩니다. 그런 후 다음 코드와 같이 업데이트된 args 배열로 push() 의 수퍼 클래스 버전이 호출됩니다.

    AS3 override function push(...args):uint 
    { 
        for (var i:* in args) 
        { 
            if (!(args[i] is dataType)) 
            { 
                args.splice(i,1); 
            } 
        } 
        return (super.push.apply(this, args)); 
    }

concat() 메서드는 passArgs 라는 임시 TypedArray를 만들어 유형 검사를 통과한 인수를 저장합니다. 이를 통해 push() 메서드에 있는 유형 검사 코드를 재사용할 수 있습니다. for..in 루프는 args 배열을 반복하며 각 인수에서 push() 를 호출합니다. passArgs 는 TypedArray 유형이므로 push() 의 TypedArray 버전이 실행됩니다. 그러면 concat() 메서드가 다음 코드와 같이 해당 수퍼 클래스 버전을 호출합니다.

    AS3 override function concat(...args):Array 
    { 
        var passArgs:TypedArray = new TypedArray(dataType); 
        for (var i:* in args) 
        { 
            // type check done in push() 
            passArgs.push(args[i]); 
        } 
        return (super.concat.apply(this, passArgs)); 
    }

splice() 메서드는 임의의 인수 목록을 취하지만 처음 두 개의 인수는 항상 삭제할 인덱스 번호 및 요소의 수를 나타냅니다. 이것이 바로 대체 splice() 메서드가 인덱스 위치 2 이상의 args 배열 요소에 대해서만 유형을 검사하는 이유입니다. 코드에서 눈 여겨 볼 것은 for 루프 내에서 splice() 에 대한 재귀 호출이 있는 것처럼 보이지만, args 가 TypedArray가 아닌 Array 유형이므로 실제로는 재귀 호출이 아니라는 점입니다. 즉, args.splice() 에 대한 호출은 메서드의 수퍼 클래스 버전에 대한 호출입니다. for..in 루프가 끝나면 다음 코드와 같이 args 배열에는 인덱스 위치 2 이상의 정확한 유형의 값만 포함되며 splice() 는 해당 수퍼 클래스 버전을 호출합니다.

    AS3 override function splice(...args):* 
    { 
        if (args.length > 2) 
        { 
            for (var i:int=2; i< args.length; i++) 
            { 
                if (!(args[i] is dataType)) 
                { 
                    args.splice(i,1); 
                } 
            } 
        } 
        return (super.splice.apply(this, args)); 
    }

unshift() 메서드는 배열의 시작 부분에 요소를 추가하며 임의의 인수 목록을 허용합니다. 대체 unshift() 메서드는 다음 예제 코드와 같이 push() 메서드에서 사용한 것과 매우 유사한 알고리즘을 사용합니다.

    AS3 override function unshift(...args):uint 
    { 
        for (var i:* in args)  
        { 
            if (!(args[i] is dataType)) 
            { 
                args.splice(i,1); 
            } 
        } 
        return (super.unshift.apply(this, args)); 
    } 
}