Система типов: абстрактные и конкретные
Как устроена иерархия типов Julia: абстрактные «категории» и конкретные типы, которые можно создавать.
Абстрактный тип задаёт категорию (например, «любое число») и не имеет экземпляров; конкретный тип описывает реальное представление данных в памяти и может быть создан.
Иерархия типов
Все типы Julia образуют дерево с корнем Any. Числовые типы, например, выстроены так: Number → Real → Integer → Int64. Верхние уровни — абстрактные, листья — конкретные. Проверить отношение можно оператором <: («подтип»):
println(Int64 <: Integer)
println(Integer <: Number)
println(Float64 <: Integer)Вывод:
true true false
Зачем нужны абстрактные типы
Абстрактные типы позволяют писать методы «для всех чисел» сразу. Метод f(x::Number) сработает и для Int, и для Float64, и для рационального, и для комплексного — для всего, что является подтипом Number. Это делает код общим, но при этом компилятор всё равно специализирует его под конкретный тип в момент вызова.
describe(x::Integer) = "$x — целое"
describe(x::AbstractFloat) = "$x — дробное"
println(describe(5))
println(describe(2.5))Вывод:
5 — целое 2.5 — дробное
Свои типы: struct
Собственный конкретный тип создают через struct. По умолчанию он неизменяем (поля нельзя менять после создания), для изменяемого пишут mutable struct:
struct Point
x::Float64
y::Float64
end
p = Point(3.0, 4.0)
println(p.x, ", ", p.y)Вывод:
3.0, 4.0
Свой тип можно объявить подтипом абстрактного: struct Dog <: Animal — и тогда он унаследует все методы, написанные для Animal.
Как работает под капотом
Ключевая идея: наследуются методы, а не поля. Абстрактный тип не имеет полей и не задаёт структуру данных — он лишь объединяет конкретные типы в категорию для диспетчеризации. Это сознательное отличие от классического ООП, где наследуют и поля, и поведение. Конкретные типы с явно указанными типами полей (как x::Float64 в Point) компилируются в плотные структуры памяти, как struct в C, — отсюда скорость. Если же тип поля не указан или абстрактен (x без аннотации = Any), компилятор не знает размер заранее и код замедляется.
Частые ошибки
Распространённая ошибка новичков — делать поля структур абстрактного типа, например x::Number или x::Real вместо x::Float64. Абстрактный тип поля лишает компилятор знания о размере и представлении данных, и быстрый код не генерируется. В «горячих» структурах указывайте конкретные типы полей или используйте параметрические типы (следующий урок).
Итоги
- Типы образуют дерево с корнем
Any: абстрактные сверху, конкретные — листья. <:проверяет и задаёт отношение «подтип».- Методы для абстрактного типа (
x::Number) работают для всех его подтипов. - Свои типы создаются через
struct(неизменяемый) илиmutable struct. - Наследуются методы, а не поля; конкретные типы полей дают скорость.