9.9 Классы
- 9.9.1 Типы классов
- 9.9.2 Выражения классов
- 9.9.3 Определения классов
- 9.9.4 Спецификации классов
- 9.9.5 Определения типов классов
Классы определяются с помощью небольшого языка, аналогичного языку модулей.
9.9.1 Типы классов
Типы классов являются аналогами выражений типов на уровне класса: они задают общую форму и свойства типов классов.
|
См. также следующие расширения языка: атрибуты и узлы расширения.
Простые выражения классов
Выражение classtype-path эквивалентно типу класса, связанному с именем classtype-path. Аналогично, выражение [ typexpr1 , … typexprn ] classtype-path эквивалентно параметрическому типу класса, связанному с именем classtype-path, в котором параметры типа были инстанцированы соответственно typexpr1, …typexprn.
Тип функции класса
Выражение типа класса typexpr -> class-type является типом функций класса (функции от значений до классов), которые принимают в качестве аргумента значение типа typexpr и возвращают в результате класс типа class-type.
Тип тела класса
Выражение типа класса object [( typexpr )] { class-field-spec } end является типом тела класса. Он задает его переменные экземпляра и методы.
Тело класса будет соответствовать типу тела класса, если оно предоставляет определения для всех компонентов, указанных в типе тела класса, и эти определения соответствуют требованиям типа, заданным в типе тела класса. Кроме того, все методы, присутствующие в теле класса, будь то виртуальные или публичные, также должны присутствовать в типе тела класса (с другой стороны, некоторые переменные экземпляра и конкретные частные методы могут быть опущены). Виртуальный метод будет соответствовать конкретному методу, что позволяет забыть его реализацию. Неизменяемая переменная экземпляра будет соответствовать изменяемой переменной экземпляра.
Локальные открытые области
Локальные открытые области поддерживаются в типах классов с OCaml 4.06.
Наследование
Конструктор наследования inherit class-body-type обеспечивает включение методов и переменных экземпляра из других типов классов. Типы переменных экземпляра и методов из class-body-type добавляются в текущий тип класса.
Спецификация переменной экземпляра
Спецификация переменной экземпляра записывается как val [mutable] [virtual] inst-var-name : typexpr, где inst-var-name — имя переменной экземпляра, а typexpr — её ожидаемый тип. Флаг mutable указывает, можно ли физически изменить эту переменную экземпляра. Флаг virtual указывает, что эта переменная экземпляра не инициализирована. Её можно инициализировать позднее через наследование.
Спецификация переменной экземпляра скроет любую предыдущую спецификацию переменной экземпляра с тем же именем.
Спецификация метода
Спецификация метода записывается как method [private] method-name : poly-typexpr, где method-name — имя метода, а poly-typexpr — его ожидаемый тип, возможно полиморфный. Флаг private указывает, что к методу нельзя получить доступ извне объекта.
Полиморфизм может быть оставлен неявным в спецификациях открытых методов: любая переменная типа, которая не привязана к параметру класса и не встречается в другом месте внутри спецификации класса, будет считаться универсальной и сделана полиморфной в результирующем типе метода. Запись явного полиморфного типа отключит это поведение.
Если для одного и того же метода присутствует несколько спецификаций, они должны иметь совместимые типы. Любая не закрытая спецификация метода принудительно делает его открытым.
Спецификация виртуального метода
Спецификация виртуального метода записывается method [private] virtual method-name : poly-typexpr, где method-name — имя метода, а poly-typexpr — его ожидаемый тип.
Ограничения на параметры типа
Конструкция constraint typexpr1 = typexpr2 заставляет два выражения типа быть равными. Это обычно используется для указания параметров типа: таким образом, они могут быть привязаны к конкретным выражениям типа.
9.9.2 Выражения класса
Выражения класса являются эквивалентом выражений значений на уровне класса: они вычисляются в классы, обеспечивая, таким образом, реализации для спецификаций, выраженных в типах классов.
|
|
См. также следующие расширения языка: локально абстрактные типы, атрибуты и узлы расширения.
Простые выражения классов
Выражение class-path вычисляет класс, связанный с именем class-path. Аналогично, выражение [ typexpr1 , … typexprn ] class-path вычисляет параметрический класс, связанный с именем class-path, в котором параметры типов соответственно инициализированы значениями typexpr1, …typexprn.
Выражение ( class-expr ) вычисляет тот же модуль, что и class-expr.
Выражение ( class-expr : class-type ) проверяет, соответствует ли class-type типу class-expr (то есть, что реализация class-expr соответствует спецификации типа class-type). Всё выражение вычисляет тот же класс, что и class-expr, за исключением того, что все компоненты, не указанные в class-type, скрыты и больше недоступны.
Применение класса
Применение класса обозначается примыканием (возможно, с метками) выражений. Это обозначает класс, конструктор которого является первым выражением, применённым к заданным аргументам. Аргументы вычисляются так же, как при применении выражения, но сам конструктор будет вычислен только при создании объектов. В частности, побочные эффекты, вызванные применением конструктора, будут происходить только во время создания объекта.
Функция класса
Выражение fun [[?]label-name:]pattern -> class-expr вычисляет функцию от значений к классам. При применении этой функции к значению v, это значение сопоставляется с образцом pattern, а результатом является результат вычисления class-expr в расширенной среде.
Преобразование из функций с умолчанием в функции с шаблонами работает аналогично для функций класса, как и для обычных функций.
Выражение
является сокращенной формой
Локальные определения
Конструкции let и let rec связывают имена значений локально, как и для выражений языка ядра.
Если локальное определение встречается в самом начале определения класса, оно будет вычислено при создании класса (точно так же, как если бы определение было вне класса). В противном случае оно будет вычислено при вызове конструктора объекта.
Локальные открытия
Локальные открытия поддерживаются в выражениях классов начиная с OCaml 4.06.
Тело класса
|
Выражение object class-body end обозначает тело класса. Это прототип объекта: оно перечисляет переменные экземпляра и методы объекта этого класса.
Тело класса — это значение класса: оно не вычисляется сразу. Вместо этого его компоненты вычисляются каждый раз при создании объекта.
В теле класса шаблон ( pattern [: typexpr] ) сопоставляется с self, поэтому предоставляя привязку для self и типа self. Self может использоваться только в методах и инициализаторах.
Тип Self не может быть закрытым типом объекта, так что класс остаётся расширяемым.
Начиная с OCaml 4.01, ошибка возникает, если одно и то же имя метода или переменной экземпляра определяется несколько раз в одном теле класса.
Наследование
Конструктор наследования inherit class-expr позволяет повторно использовать методы и переменные экземпляра из других классов. Выражение класса class-expr должно вычисляться в тело класса. Переменные экземпляра, методы и инициализаторы из этого тела класса добавляются в текущий класс. Добавление метода переопределит любой ранее определённый метод с тем же именем.
Предок может быть связан, добавив as lowercase-ident к конструкции наследования. lowercase-ident не является истинной переменной и может использоваться только для выбора метода, т.е. в выражении lowercase-ident # method-name. Это даёт доступ к методу method-name так, как он был определён в родительском классе, даже если он переопределён в текущем классе. Область действия этой привязки предка ограничена текущим классом. К методу предка можно обратиться из подкласса, но только косвенно.
Определение переменной экземпляра
Определение val [mutable] inst-var-name = expr добавляет переменную экземпляра inst-var-name со значением по умолчанию — значение выражения expr. Флаг mutable позволяет физическое изменение этой переменной методами.
Переменная экземпляра может использоваться только в методах и инициализаторах, которые следуют за её определением.
Начиная с версии 3.10, повторные определения видимой переменной экземпляра с тем же именем не создают новую переменную, но объединяются, используя последнее значение для инициализации. Они должны иметь одинаковые типы и изменяемость. Однако, если переменная экземпляра скрыта, опуская её из интерфейса, она будет оставаться отличной от других переменных экземпляра с тем же именем.
Виртуальное определение переменной экземпляра
Спецификация переменной записывается как val [mutable] virtual inst-var-name : typexpr. Она указывает, является ли переменная изменяемой, и даёт её тип.
Виртуальные переменные экземпляра были добавлены в версии 3.10.
END_OF_DOCUMENT_MARKERОпределение метода
Определение метода записывается как метод имя-метода = выражение. Определение метода переопределяет любое предыдущее определение этого метода. Метод будет общедоступным (то есть не закрытым), если это указано в любом из определений.
Закрытый метод, метод закрытый имя-метода = выражение, — это метод, который может быть вызван только на объекте self (из других методов того же объекта, определённых в этом классе или одном из его подклассов). Этот вызов выполняется с помощью выражения имя-значения # имя-метода, где имя-значения напрямую связано с self в начале определения класса. Закрытые методы не отображаются в типах объектов. Метод может иметь как общедоступные, так и закрытые определения, но как только появляется общедоступное определение, все последующие определения будут общедоступными.
Методы могут иметь явным образом полиморфный тип, позволяющий использовать их полиморфно в программах (даже для одного и того же объекта). Явное объявление может быть выполнено одним из трёх способов: (1) указанием явного полиморфного типа в определении метода сразу после имени метода, например метод [закрытый] имя-метода : { ' идентификатор }+ . тип-выражение = выражение; (2) путём предварительного объявления явного полиморфного типа с помощью определения виртуального метода; (3) импортированием такого объявления через наследование и/или ограничение типа self.
В телах методов доступны некоторые специальные выражения для работы с переменными экземпляра и дублирования self:
|
Выражение имя-переменной-экземпляра <- выражение изменяет текущий объект на месте, заменяя значение, связанное с имя-переменной-экземпляра, значением выражение. Разумеется, эта переменная экземпляра должна быть объявлена изменяемой.
Выражение {< имя-переменной-экземпляра1 = выражение1 ; … ; имя-переменной-экземпляраn = выражениеn >} вычисляет копию текущего объекта, в которой значения переменных экземпляра имя-переменной-экземпляра1, …, имя-переменной-экземпляраn были заменены значениями соответствующих выражений выражение1, …, выражениеn.
Определение виртуального метода
Спецификация метода записывается как метод [закрытый] виртуальный имя-метода : полиморфное-тип-выражение. Она указывает, является ли метод общедоступным или закрытым, и задаёт его тип. Если метод предназначен быть полиморфным, тип должен быть явно полиморфным.
Явное переопределение
С Ocaml 3.12, ключевые слова inherit!, val! и method! имеют ту же семантику, что и inherit, val и method, но дополнительно требуют, чтобы определение, которое они вводят, было переопределением. Иными словами, method! требует, чтобы имя-метода уже было определено в этом классе, val! требует, чтобы имя-переменной-экземпляра уже было определено в этом классе, а inherit! требует, чтобы класс-выражение переопределял некоторые определения. Если такого переопределения не происходит, выдаётся ошибка.
В качестве побочного эффекта, эти 3 ключевых слова избегают предупреждений 7 (переопределение метода) и 13 (переопределение переменной экземпляра). Обратите внимание, что предупреждение 7 по умолчанию отключено.
Ограничения для параметров типа
Конструкт ограничение тип-выражение1 = тип-выражение2 заставляет два выражения типа быть равными. Это обычно используется для задания параметров типа: таким образом они могут быть связаны со специфическими выражениями типа.
Инициализаторы
Инициализатор класса инициализатор выражение задаёт выражение, которое будет вычислено при создании объекта из класса, после инициализации всех его переменных экземпляра.
9.9.3 Определения классов
|
Определение класса класс связывание-класса { и связывание-класса } рекурсивно. Каждое связывание-класса определяет имя-класса, которое может быть использовано во всём выражении, кроме наследования. Его также можно использовать для наследования, но только в определениях, следующих за своим собственным.
END_OF_DOCUMENT_MARKERСвязывание класса связывает имя класса имя-класса со значением выражения выражение-класса. Оно также связывает тип класса имя-класса с типом класса и определяет два сокращения типов: имя-класса и # имя-класса. Первое — это тип объектов данного класса, а второе — более общее, поскольку оно объединяется с типом любого объекта, принадлежащего подклассу (см. раздел 9.4).
Виртуальный класс
Класс должен быть помечен как виртуальный, если один из его методов является виртуальным (то есть, он присутствует в типе класса, но фактически не определён). Объекты не могут быть созданы из виртуального класса.
Параметры типа
Параметры типа класса соответствуют параметрам типа класса и двум сокращениям типов, определённым связыванием класса. Они должны быть связаны с фактическими типами в определении класса с помощью ограничений типов. Для того чтобы сокращения были корректны, переменные типа выведенного типа класса должны быть либо параметрами типа, либо быть связаны в разделе ограничений.
9.9.4 Спецификации классов
|
Это аналог в сигнатурах определений классов. Спецификация класса соответствует определению класса, если у них одинаковые параметры типа и их типы совпадают.
9.9.5 Определения типов классов
|
Определение типа класса class имя-класса = тип-тела-класса определяет сокращение имя-класса для типа тела класса тип-тела-класса. Как и для определений классов, также определены два сокращения типов имя-класса и # имя-класса. Определение может быть параметризовано некоторыми параметрами типа. Если какой-либо метод в теле типа класса является виртуальным, определение должно быть помечено virtual.
Два определения типа класса совпадают, если у них одинаковые параметры типа и они расширяются до совпадающих типов.
© 1995-2022 INRIA.
https://v2.ocaml.org/releases/4.14/htmlman/classes.html