Универсальные шаблоны(Generics)

Vala поддерживает систему обобщений времени выполнения, с помощью которой определенный экземпляр класса может быть ограничен определенным типом или набором типов, заданных во время его создания. Это ограничение обычно используется, например, для хранения в списке объектов только определенного типа, т.к. Vala будет следить за тем, чтобы объекты именно указанного типа были занесены в список и при изъятии, они будут приводится к указанному типу.

В Vala обобщения обрабатываются во время выполнения. Когда вы определяете класс, который ограничен по определенному типу, то будет существовать только один класс, и много экземпляров от него. Это отличается от C++, где на каждое ограничение по типу создается новый класс - подход в Vala более близок к Java. Наиболее важное следствие этого в том, что статические члены везде используются одинаково, независимо от ограничений, устанавливаемых в каждом экземпляре, и что определенное в потомке обобщение может быть использовано как обобщение, созданное в базовом классе.

Следующий код демонстрирует, как использовать обобщенную систему для написания минимального класса-обвертки:

public class Wrapper<G> : GLib.Object {
    private G data;

    public void set_data(G data) {
        this.data = data;
    }

    public G get_data() {
        return this.data;
    }
}

Класс Wrapper при создании должен ограничиваться типом, в данном случае тип обозначается как G, поэтому экземпляры этого класса будут хранить один объект типа G, и иметь методы для получения и изменения этого объекта (Цель этого примера показать, что обобщенный класс не может использовать свойства для своего ограничивающего типа, поэтому здесь использованы простые методы get и set.)

При создании объекта данного типа нужно выбрать тип, например встроенный тип string (в Vala не существует ограничений на тип используемого обобщения):

var wrapper = new Wrapper<string>();
wrapper.set_data("test");
var data = wrapper.get_data();

Как видите, при извлечении данных они присваиваются идентификатору без явного типа. Это возможно благодаря тому, что Vala знает, какого типа объекты находятся внутри экземпляра класса Wrapper.

То, что Vala не создает множество классов по вашему определению обобщения позволяет сделать следующее:

class TestClass : GLib.Object {
}

void accept_object_wrapper(Wrapper<Glib.Object> w) {
}

...
var test_wrapper = new Wrapper<TestClass>();
accept_object_wrapper(test_wrapper);
...

Так как все экземпляры TestClass являются так же экземплярами Object метод accept_object_wrapper с радостью обработает переданный ему объект, и будет обращаться с ним как с экземпляром GLib.Object.

Last updated