Сложные типы данных

До этого момента мы встречались со следующими типами данных:

  • экземпляры классов
  • экземпляры перечислений
  • dynamic

В дополнение к этому, есть несколько важных типов данных.

Анонимный тип

Анонимный тип - это тип объявленного анонимно объекта. Также, это тип идентификатора класса (соответствующий всем его статическим полям) или идентификатора перечисления (включающий все его конструкторы). Вот пример, который это поясняет:

    enum State {
        on;
        off;
        disable;
    }

    class C {
        static var x : Int;
        static var y : String;

        function f() {
            // выведет { id : Int, city : String }
            type({ id : 125, city : "Kyoto" });
        }

        function g() {
            // выведет { on : State, off : State, disable : State }
            type(State);
        }

        function h() {
            // выведет { x : Int, y : String }
            type(C);
        }
    }

Typedef

Для сокращения записи, можно задать имя анонимному типу или типу данных с длинным названием:

typedef User = {
    var age : Int;
    var name : String;
}
// ....
var u : User = { age : 26, name : "Tom" };

// PointCube - трехмерный массив точек
typedef PointCube = Array<Array<Array<Point>>>

Typedef не являются классами и используются только для задания типов.

Связывание типов данных

Можно связать типы данных, используя определения в следующем стиле:

class MyClassWithVeryLongName { }
typedef MyClass = MyClassWithVeryLongName;

Любой тип может быть задан таким образом - не только класс, но и интерфейс, перечисление и другие определения типа. Получившееся определение типа можно использовать в любом месте вместо class, enum, или чего бы то ни было.

Приватные члены

Typedef могут содержать приватные члены в своем определении. Это откроет доступ к недоступным другим образом членам:

class Foo {
    public function new() {}
    public function fullAccess() return true
    function restrictedAccess() return true
}

typedef Bar = {
    function fullAccess():Bool;
    private function restrictedAccess():Bool;
}

class Main {
    public static function main()
    {
        var foo = new Foo();
        foo.fullAccess();
        //Ошибка компиляции - доступ к приватному члены класса restrictedAccess
        foo.restrictedAccess();
        
        var bar:Bar = foo;
        bar.fullAccess();
        //Это разрешено
        bar.restrictedAccess();
    }
}

Private types

A type - class, typedef, interface or enum - can be defined as 'private', which will confine its visibility only to the module it is defined in. Public types belong to packages they are defined in, and they can only be defined once. With private type definitions however you can have a "local" type only visible within the module it is defined in. Another private type with the same name and in the same package can be defined in another module.

private typedef MyType =
{

}

Accessing Typedefs From Other Packages


It is often useful to use a typedef from another package for a variable declaration. This can be accomplished in the same ways that internal helper classes are referenced. For a given typedef "MyType":
// in Foo.hx
private typedef MyType =
{ ... }

The first way is to simply import the Class containing the Typedef you wish to use:

// in Bar.hx
import Foo;
class Bar{
    var mt:MyType;
}

The second way is to use a special extended package definition, which allows you to avoid using import:

// in Bar.hx
class Bar{
    var mt:Foo.MyType;
}

Functions

When you want to define function types to use them as variables, you can define them by listing the arguments followed by the return type and separated with arrows. For example Int -> Void is the type of a function taking an Int as argument and returning Void. Color -> Color ->Int takes two Color arguments and returns an Int.

    class C {
        function f(x : String) : Int {
            // ...
        }

        function g() {
            type(f); // print String -> Int
            var ftype : String -> String = f;
            // ERROR : should be String -> Int
        }
    }

Unknown

When a type is not declared, it is used with the type Unknown. The first time it is used with another type, it will change to it. This was explained in more details in type inference. The id printed with the Unknown type is used to differentiate several unknowns when printing a complex type.

    function f() {
        var x;
        type(x); // print Unknown<0>
        x = 0;
        type(x); // print Int
    }

The diversity of types expressible with Haxe enable more powerful models of programming by providing high-level abstractions that don't need complex classes relationships to be used.

Extensions

Extensions can be used to extend either a typedef representing an anonymous type or to extend a class on-the-fly.

Here's an example of anonymous typedef extension :

   typedef Point = {
       var x : Int;
       var y : Int;
   }

   // define 'p' as a Point with an additional field z
   var p : {> Point, z : Int }
   p = { x : 0, y : 0, z : 0 }; // works
   p = { x : 0, y : 0 }; // fails

For classes, since they don't define types, you need to use a cast when assigning, it's unsafe so be careful :

   var p : {> flash.MovieClip, tf : flash.TextField };
   p = flash.Lib._root; // fails
   p = cast flash.Lib._root; // works, but no typecheck !

You can also use extensions to create cascading typedefs :

    typedef Point = {
        var x : Int;
        var y : Int;
    }
    typedef Point3D = {> Point,
        var z : Int;
    }

In that case, every Point3D will of course be a Point as well.

Cascading typedefs can be created from normal class types as well, but the compiler will not be able to match this typedef against an anonymous object, or any other instanced class. This is because Haxe is not fully structurally subtyped. Structural subtyping can only occur between anonymous typedefs or between an instance and an anonymous typedef.

«« Тип Dynamic | Итераторы »»

version #10388, modified 2011-04-02 23:12:11 by Scythian