Выбрать главу

def show(value : String)

    puts "The string is '#{value}'"

end

def show(value : Int)

    puts "The integer is #{value}"

end

show(12) # => The integer is 12

show("hey") # => The string is 'hey'

show(3.14159) # Error: no overload matches 'show' with type Float64

x = rand(1..2) == 1 ? "hey" : 12

show(x) # => Either "The integer is 12" or "The string is 'hey'"

Параметр можно ограничить типом, написав его после двоеточия. Обратите внимание, что пробел до и после двоеточия обязателен. Типы будут проверяться всякий раз, когда метод вызывается для обеспечения корректности. Если предпринята попытка вызвать метод с недопустимый тип, он будет обнаружен во время компиляции и выдаст правильное сообщение об ошибке.

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

Последняя строка показывает концепцию множественной диспетчеризации в Crystaclass="underline" если аргумент вызова тип объединения (в данном случае Int32 | String), и метод имеет несколько перегрузок, компилятор сгенерирует код для проверки фактического типа во время выполнения и выбора правильного реализация метода.

Мультидиспетчеризация также произойдет в иерархии типов, если выражение аргумента имеет абстрактный родительский тип, и для каждого возможного конкретного типа определен метод. В следующей главе вы узнаете больше об определении иерархии типов.

Ограничение типа аналогично аннотациям типов в большинстве других языков, где вы укажите фактический тип параметра. Но в Crystal нет аннотаций типов. Здесь важно слово «ограничение»: ограничение типа служит для ограничения возможных типы приемлемы. Фактический тип по-прежнему исходит из места вызова. Посмотрите это, например:

def show_type(value : Int | String)

    puts "Compile-time type is #{typeof(value)}."

    puts "Runtime type is #{value.class}."

    puts "Value is #{value}."

end

show_type(10)

# => Compile-time type is Int32.

# => Runtime type is Int32.

# => Value is 10.

x = rand(1..2) == 1 ? "hello" : 5_u8

show_type(x)

# => Compile-time type is (String | UInt8).

# => Runtime type is String.

# => Value is hello.

Интересно видеть, что тело метода всегда специализировано для типов, используемых в вызывайте сайт, не требуя проверок во время выполнения или какого-либо динамизма. Это часть того, что делает Кристалл очень быстрый язык.

Вы также можете применить ограничения типа к возвращаемому типу метода; это будет гарантировать, что метод ведет себя так, как ожидалось, и выдает правильные данные. Посмотрите это, например:

def add(a, b) : Int

    a + b

end

add 1, 3 # => 4

add "a", "b" # Error: method top-level add must return Int but it is returning String

Здесь вариант строки не удастся скомпилировать, поскольку a + b создаст строку, но метод ограничен возвратом Int. Помимо типа, параметры также могут иметь значения по умолчанию.

Значения по умолчанию

Методы могут иметь значения по умолчанию для своих аргументов; это способ пометить их как необязательные. Для этого укажите значение после имени параметра, используя символ равенства. Посмотрите это, например:

def random_score(base, max = 10)

    base + rand(0..max)

end

p random_score(5) # => Some random number between 5 and 15.

p random_score(5, 5) # => Some random number between 5 and 10.

Вы можете использовать значение по умолчанию, если метод имеет наиболее распространенное значение, но вы все равно хотите, чтобы при необходимости можно было передавать разные значения. Если параметров много со значениями по умолчанию рекомендуется давать им имена.

Именованные параметры

Когда метод вызывается с множеством аргументов, иногда может быть непонятно, что означает каждый из них. Чтобы улучшить это, параметры могут быть названы в месте вызова. Вот пример:

# These are all the same:

p random_score(5, 5)

p random_score(5, max: 5)

p random_score(base: 5, max: 5)

p random_score(max: 5, base: 5)

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

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