方法¶
为了避免重复相同的消息,我们可以定义一个方法并多次调用它,而不是使用变量。
方法定义由关键字 def
后跟方法名表示。从关键字 end
开始到结束的所有表达式都是方法体的一部分。
def say_hello
puts "Hello Penny!"
end
say_hello
say_hello
say_hello() # syntactically equivalent method call with parentheses
提示
方法调用明确地由名称后面的括号表示,但可以省略。只有在需要消除歧义时才需要,例如,如果 say_hello
也是一个局部变量。
参数¶
如果我们想问候不同的人,但都是以相同的方式?我们可以定义一个方法,通过参数允许自定义,而不是编写单独的消息。参数就像方法体内部的局部变量。参数在方法名后面的括号中声明。调用方法时,可以传入作为方法参数值映射的参数。
def say_hello(recipient)
puts "Hello #{recipient}!"
end
say_hello "World"
say_hello "Crystal"
提示
方法调用中的参数通常放在括号中,但通常可以省略。say_hello "World"
和 say_hello("World")
在语法上是等价的。
一般建议使用括号,因为它可以避免歧义。但是,如果表达式读起来像自然语言,它们经常会被省略。
默认参数¶
可以为参数分配默认值。如果方法调用中缺少参数,则使用它。通常,参数是必需的,但当存在默认值时,可以省略它。
def say_hello(recipient = "World")
puts "Hello #{recipient}!"
end
say_hello
say_hello "Crystal"
类型限制¶
我们的示例方法期望 recipient
是一个 String
。但任何其他类型也可以工作。例如,尝试 say_hello 6
。
这对这个方法来说不一定是问题。使用任何其他类型都是有效的代码。但从语义上来说,我们希望以 String
类型的姓名来问候人们。
类型限制限制了参数允许的类型。它们出现在参数名后面,用冒号分隔。
def say_hello(recipient : String)
puts "Hello #{recipient}!"
end
say_hello "World"
say_hello "Crystal"
# Now this expression doesn't compile:
# say_hello 6
现在名称不能再是数字或其他数据类型了。但这并不意味着你不能用数字作为名字来问候别人。数字只需要用字符串表示即可。例如,尝试 say_hello "6"
。
重载¶
限制参数的类型可用于位置重载。当方法具有未限制的参数,如 say_hello(recipient)
时,对方法 say_hello
的所有调用都会转到该方法。但是,通过重载,可以存在具有不同参数类型限制的多个同名方法。每个调用都路由到最适合的重载。
# This methods greets *recipient*.
def say_hello(recipient : String)
puts "Hello #{recipient}!"
end
# This method greets *times* times.
def say_hello(times : Int32)
puts "Hello " * times
end
say_hello "World"
say_hello 3
重载不仅由类型限制定义。参数的数量以及命名参数也是相关特征。
返回值¶
方法返回一个值,该值成为方法调用的值。默认情况下,它是方法中最后一个表达式的值
def adds_2(n : Int32)
n + 2
end
puts adds_2 40
方法可以使用 return
语句在它主体中的任何位置返回。传递给 return
的参数将成为方法的返回值。如果没有参数,则为 nil
。
以下示例说明了显式和隐式 return
的使用
# This method returns:
# - the same number if it's even,
# - the number multiplied by 2 if it's odd.
def build_even_number(n : Int32)
return n if n.even?
n * 2
end
puts build_even_number 7
puts build_even_number 28
返回值类型¶
让我们开始定义一个方法,我们期望它将返回一个 Int32
值,但错误地返回一个 String
def life_universe_and_everything
"Fortytwo"
end
puts life_universe_and_everything + 1 # Error: no overload matches 'String#+' with type Int32
因为我们从来没有告诉编译器我们期望方法返回一个 Int32
,所以编译器能做的最好的就是告诉我们没有 String#+
方法接受 Int32
值作为参数(即编译器指向使用该值的地方,而不是指向错误的根源:方法返回值类型)。
如果使用类型信息,错误消息可以更准确,所以让我们再次尝试该示例,但现在指定类型
def life_universe_and_everything : Int32
"Fortytwo"
end
puts life_universe_and_everything + 1 # Error: method top-level life_universe_and_everything must return Int32 but it is returning String
现在编译器可以准确地向我们显示问题的根源。正如我们所见,提供类型信息对于在编译时发现错误非常有用。