Цейлонская метамодель

Я изучаю Цейлон и у меня есть вопросы по поводу его метамодели. Я хочу создать некоторый базовый класс «DataContainer», который позволяет создавать экземпляры неизменяемых классов со встроенной реализацией equals-hash: например. Идентификатор (125, "ab") == Идентификатор (125, "ab") Таким образом, базовый класс должен собирать все общие непеременные значения и использовать эту информацию в методах "хэш" и "равно". Я написал этот код:

shared abstract class DataContainer(ClassDeclaration declaration) {
    value members = {
        for (i in declaration.memberDeclarations<ValueDeclaration>())
            if (!i.variable, i.name != "hash", i.name != "string") i
    };
    variable Integer? hashCode = null;

    shared actual Boolean equals(Object that) {
        if (is DataContainer that) {
            for (item in members) {
                value thisMember = item.memberGet(this);
                value thatMember = item.memberGet(that);
                if (exists thisMember, exists thatMember) {
                    if (thisMember != thatMember) { return false; }
                } else if (thisMember exists != thatMember exists) { return false; }
            }
            return true;
        }
        return false;
    }

    shared actual Integer hash => hashCode else (hashCode = calculateHash());

    Integer calculateHash() {
        variable value result = 0;
        for(i in members) {
            if (exists member = i.memberGet(this)) {
                result = result.xor(member.hash);
            }
        }
        return result;
    }
}

class Identifier(shared Integer? id, shared String? name) extends DataContainer(`class`) {}

Класс Identifier является клиентом DataContainer. Мне нравится это решение в целом, но я должен передать «класс» в конструктор суперкласса, потому что, если я использую «класс» внутри DataContainer, он не видит никаких членов подкласса. Как я могу получить фактический список членов расширенного класса в методах базового класса? Что-то вроде "это" не работает...


person Alexander Kornilov    schedule 29.11.2017    source источник


Ответы (2)


Я нашел решение благодаря ребятам из сообщества Ceylon. Вместо «класса» следует использовать функцию classDeclaration(this) из ceylon.language.meta.

Это окончательный код:

shared abstract class DataContainer() {
    variable Integer? _hash = null;
    variable ValueDeclaration[]? _members = null;

    shared actual Boolean equals(Object that) {
        if (is DataContainer that) {
            for (i in members) {
                value thisMember = i.memberGet(this);
                value thatMember = i.memberGet(that);
                if (exists thisMember, exists thatMember) {
                    if (thisMember != thatMember) { return false; }
                } else if (thisMember exists != thatMember exists) { return false; }
            }
            return true;
        }
        return false;
    }

    shared actual Integer hash => _hash else (_hash = calculateHash());

    ValueDeclaration[] members => _members else (_members = [
        for (i in classDeclaration(this).memberDeclarations<ValueDeclaration>())
            if (!i.variable, i.name != "string", i.name != "hash") i
    ]);

    Integer calculateHash() {
        variable Integer result = 0;
        for (i in members) {
            if (exists member = i.memberGet(this)) {
                result = result.xor(member.hash);
            }
        }
        return result;
    }
}

class Identifier(shared Integer? number, shared String? name) extends DataContainer() {}
person Alexander Kornilov    schedule 01.12.2017
comment
может быть, DataContainer extends Object() ? - person Jonas Berlin; 10.09.2018

В дополнение к грозному ответу Александра я позволил себе также реализовать функцию string, чтобы вы могли просто print(myObj) и она хорошо отображалась:

shared default actual String string {
    value s = StringBuilder();
    s.append(type(this).declaration.name).append(" { ");
    for (i in members) {
        value m = i.memberGet(this);
        s.append(i.name).append(":");
        s.append(if(exists m) then m.string else "<null>");
    }
    return s.append(" }").string;
}
person Jonas Berlin    schedule 13.09.2018