【在主画面加入捷径】
       
【选择语系】
繁中 简中

Perl 6 程序设计教学:多态 (Polymorphism)

【赞助商连结】

    Duck type

    Duck type 是动态类型语言的一种特性,duck type 物件不需在意其实际的类,仅需在意该类是否有提供相对应的公开方法。如以下实例:

    class Duck {
        method speak {
            "Pack pack".say;
        }
    }
    
    class Dog {
        method speak {
            "Wow wow".say;
        }
    }
    
    class Tiger {
        method speak {
            "Halum halum".say;
        }
    }
    
    my @animals = (
            Duck.new,
            Dog.new,
            Tiger.new,
        );
        
    for @animals -> $a {
        $a.speak;
    }
    

    在这个例子中,只要 @animals 数组中的物件有实现 speak 方法即可,我们不需要去检查各个物件实际的类。

    对于从静态类型语言转换过来的程序设计者,时常有检查物件所属的类的冲动;然而,过度地检查类型,便失去使用动态类型语言的优点。如果想确保类型安全,建议使用 role 来约束类的行为,如同我们前文所举的例子。

    子类型

    子类型 (subtyping) 也是一种实现多态的方法,可以透过继承或 roles 来实现。由于 Perl 6 是动态类型语言,本身已经支援 duck typing,不太需要使用子类型实现多态。如果想实现子类型,建议使用 role。

    ##函数重载

    使用 multi 可以声明同名但不同参数的函式或方法。我们在先前的范例中已经展示过其用法。

    运算符重载

    透过运算符重载,衍生类也可以像内建类般,使用运算符来操作类;运算符重载常用在数学相关的类,像是向量 (vector) 或矩阵 (matrix) 等。以下实例实现向量加法:

    role IVector {
        method elems { ... }
        method at($i) { ... }
    }
    
    class Vector does IVector {
        has Numeric @!vec;
        
        submethod BUILD(:array(@a)) {
            @!vec = @a;
        }
        
        method elems {
            @!vec.elems;
        }
        
        method at($i) {
            return @!vec[$i];
        }
    }
    
    # Overloading indexing method.
    multi sub postcircumfix:<[ ]>(IVector $v, $i) {
        $v.at($i);
    }
    
    # Overloading addition method.
    multi sub infix:<+>(IVector $p, IVector $q) {
        if $p.elems != $q.elems {
            die "Unequal vector length";
        }
            
        my @out;
            
        loop (my $i = 0; $i < $p.elems; $i++) {
            @out.push($p[$i] + $q[$i])    
        }
            
        Vector.new(array => @out);
    }
    
    my $p = Vector.new(array => (1, 2, 3));
    my $q = Vector.new(array => (2, 3, 4));
    my $v = $p + $q;
    $v[0] == 3 or die "Wrong value";
    $v[1] == 5 or die "Wrong value";
    $v[2] == 7 or die "Wrong value";
    

    某种程度来说,运算符重载偏向使用者自订的语法糖,而非必备的语法特性。有许多现代语言支援运算符重载,但也有语言不支援,像是 Java 和 Go。Perl 6 的运算符重载相当灵活,甚至可以自订新的运算符;但笔者对于自订运算符的态度较为保守,过度地使用运算符重载,反而会造成代码难以阅读。

    泛型

    由于 Perl 6 是动态类型语言,不太需要使用泛型程序,即可将相同程序代码套用在不同类型上。Perl 6 的 paramaterized role 提供有限度的泛型程序支援,但不若其他语言的泛型系统来得完整,官方对此也着墨甚少,故此处不深入介绍。