Perl6 — Классы

    1. Особенности работы с переменными и литералами в Perl6
    2. Perl6 — Операции над переменными, анонимные блоки
    3. Perl6 — Условные операторы, циклы
    4. Perl6 — Работа с функциями

    Последние несколько дней я читал раздел по работе с объектами в Perl6. К сожалению я так и не смог понять некоторые моменты, и надеюсь что те кто уже смог их понять подскажут, в чем же дело. Как и раньше, все описанные возможности будут сопровождаться небольшими экспериментами или примерами, чтобы пояснить как это работает, или как нужно использовать.
    Приступим:


    В Perl6 есть два возможных способа объявить класс:
    -сделать весь файл описанием класса
    class Name;
    has $.field;
    ...
    method Func() {...};

    -объявить класс с помощью конструкции
    class Name
     {
         has $.field;
     }

    Можно как и в языке C++ указать лишь имя класса, дав его описание позднее:
    class Name {...};
    my $var = Name.new;
    class Name {has $.elem};

    Если последней строчки не будет то произойдет ошибка при компиляции
    Так же есть возможность сделать следующее:
    my $className = class Name
     {
         has $.field is rw;
     }
    my $var1 = Name.new;
    my $var2 = $className.new;
    $var1.field = 10;
    $var2.field = 5;
    say $var1.field, ' ', $var2.field;

    В результате увидим строку '10 5'
    Можно создать анонимный класс с помощью конструкции
    my $anonClass = class :: {has $.field};
    my $var = $anonClass.new;

    Уже созданные переменные могут быть использованы для создания новых переменных:
    my $var1 = ClassName.new;
    my $var2 = $var1.new;

    Однако создание происходит без копирования данных.

    Теперь подробнее о том что можно указывать при объявлении:
    для указания поля используется ключевое слово has:
    class Name
     {
         has Int $.publicMember;
         has $!privateMember;
         has @.publicArray is rw;
         has %!privateHash;
         has $.a is rw;
         has $.b is rw = 'Значение по умолчанию';
     }
    my $v = Name.new;
    say $v.publicMember;  #выведет "Int()"
    #$v.publicMember = 10 - Ошибка, переменная readonly
    #say $v!privateMember - Ошибка
    $v.mas = 1, 2, 3;
    #say $v!privateHash - Ошибка
    $v.a = 'new value';
    say $v.b  #Выведет "Значение по умолчанию"

    Так же стоит отметить, что в теле метода можно изменять приватные переменные, даже если указано is readonly (Однако я не могу сказать, это так и задумано, или сказывается незаконченность вирт. машины)

    Методы у классов указываются ключевым словом method:
    class Name
     {
         has $!privateMember = 5;
    
         method GetPrivateMember()
          {
              return $!privateMember;
          }
          
         method SetPrivateMember($value)
          {
              $!privateMember = $value if $value > 0;
          }
     }
    my $var = Name.new;
    say $var.GetPrivateMember;
    $var.SetPrivateMember(10);
    $var.SetPrivateMember: 20;


    Методы могут быть объявлены приватными:
    method !PrivateMethod()
     {
         say 'Hello!';
     }

    Можно указать через какую переменную будет доступен объект (инвокант), у которого вызывается метод:
    method Func($self: $param1, $param2)
     {
         $self.Func2($param1);
     }

    Имя переменной не обязательно должно быть $self
    method Func($someName: $param1, $param2)
     {
         $someName!PrivateMethod($param1);
     }

    Если эту переменную не указывать то сам объект будет доступен как 'self'
    method Func($param1, $param2)
     {
         self.PublicMethod($param1);
         self!PrivateMethod($param2);
     }


    Для вызова в теле метода другого метода этого класса используется следующая конструкция
    self!PrivateMethod();
    self.PublicMethod();

    Например
    class Name
     {
         method !A()
          {
              say "!A";
          }
         
         method A($s:)
          {
              $s!A();
          }
          
         method B()
          {
              self!A;
              self.C;
              say "B";
          }
          
         method C()
          {
              say "C";
          }
     }

    При этом возможно делать вызовы self.A и self!A

    Для доступа к полям класса в теле метода используется теже самые имена, что и при объявлении:
    class Name
     {
         has $.a;
         has $!b;
         
         method PrintValues()
          {
              say $.a;
              say $!b;
          }
     }

    Также можно написать self.a но нельзя написать self!b, т.к. в этом случае будет подразумеваться вызов приватного метода b, а не обращение к переменной b

    Если имени класса при объявлении указать is rw то все поля, которые будут содержаться в этом классе станут доступны для записи без указания is rw для каждого поля в отдельности:
    class Name is rw
     {
         has $.a;
         has $.b;
     }


    Также в классах можно объявлять submethod'ы. Отличие их от обычных методов в том, что submethod наследоваться дочерним классом не будет.

    Теперь о конструкторах
    Можно указать какие действия выполнить при создании нового экземпляра класса:
    class Name
     {
         has $.arg1 is rw = 10;
         has $!arg2;
         submethod BUILD(:$namedArg1, :$namedArg2)
          {
              self.arg1 = $namedArg1 if $namedArg1>0;
              $!arg2 = $namedArg2;
          }
     }
    my $var = Name.new(:namedArg2(200), :namedArg1<Hello>);

    Есть одна вещь которая мне непонятна: мне пришлось писать self.arg1 а не $.arg1, т.к. мне выдавалась ошибка «Virtual call $.a may not be used on partially constructed objects».
    Я не могу понять, так и задумано или же это просто ещё не до конца проработанная часть. Но в любом случае, такой конструктор работает нормально.

    Также можно указывать деструктор, который будет вызываться автоматически GarbageCollector'ом.
    submethod DESTROY()
     {
         say 'DESTROY';
     }

    Но на этот раз, вышеуказанной надписи при проведении экспериментов я так и не смог увидеть. Подозреваю что эта часть тоже ещё не доделана.

    В Perl6 наследование организуется следующей конструкцией:
    class Name {has $.a};
    class Name2 is Name {has $.b};
    my $var = Name2.new;
    say $var;  #Name2.new(b => Any, a => Any)


    Возможно множественное наследование, в результате которого может получиться что имена функций или полей в классах будут совпадать:
    class Name
     {
         has $.a is rw = 10;
         has $.c is rw = 15;
         method A() {say "Name ", $.a;}
     }
    class Name2
     {
         has $.b is rw = 20;
         has $.c is rw = 25;
         method A() {say "Name2 ", $.b;}
     }
    class Name3 is Name is Name2
     {
         method A()
          {
              self.Name2::A();
              say $.c;
          }
     }
    my $var = Name3.new;
    $var.a = 100;
    $var.b = 200;
    $var.A;

    В данном случае будут выведены на экран две строчки:
    Name2 200
    15

    Если не переопределять функцию A в дочернем класе, то функция Name::A перекроет функцию Name2::A, также происходит с переменной $.c
    Как я понимаю строка self.Name2::A(); выполняет метод Name2::A() для объекта self, однако все мои догадки, как это работает портит тот факт, что на имя self.Name2 выдается ошибка, а так же нельзя написать self.(&Name2::A) или что-то вроде этого. Буду рад если кто-нибудь из знающих людей подскажет.

    Конечно описанные здесь возможности это лишь часть из того что можно делать с объектами (например я даже не упомянул о делегировании, ролях — аналогов интерфейсов в с++), но пока что я не хотел бы так сильно углубляться в эту тему, а продолжить изучение других возможностей Perl6. А пока что для начала освоения языка хватит и описанного.
    Ads
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More

    Comments 0

    Only users with full accounts can post comments. Log in, please.