继承¶
除 Object
(层次结构的根节点)之外的所有类都继承自另一个类(其超类)。如果您没有指定超类,则默认情况下类继承自 Reference
,结构体继承自 Struct
。
一个类继承了超类中的所有实例变量,以及所有实例和类方法,包括其构造函数 (new
和 initialize
)。
class Person
def initialize(@name : String)
end
def greet
puts "Hi, I'm #{@name}"
end
end
class Employee < Person
end
employee = Employee.new "John"
employee.greet # "Hi, I'm John"
如果一个类定义了 new
或 initialize
,则不会继承其超类的构造函数
class Person
def initialize(@name : String)
end
end
class Employee < Person
def initialize(@name : String, @company_name : String)
end
end
Employee.new "John", "Acme" # OK
Employee.new "Peter" # Error: wrong number of arguments for 'Employee:Class#new' (1 for 2)
您可以在派生类中覆盖方法
class Person
def greet(msg)
puts "Hi, #{msg}"
end
end
class Employee < Person
def greet(msg)
puts "Hello, #{msg}"
end
end
p = Person.new
p.greet "everyone" # "Hi, everyone"
e = Employee.new
e.greet "everyone" # "Hello, everyone"
您可以使用类型限制来定义专门的方法,而不是覆盖方法
class Person
def greet(msg)
puts "Hi, #{msg}"
end
end
class Employee < Person
def greet(msg : Int32)
puts "Hi, this is a number: #{msg}"
end
end
e = Employee.new
e.greet "everyone" # "Hi, everyone"
e.greet 1 # "Hi, this is a number: 1"
super¶
您可以使用 super
调用超类的方法
class Person
def greet(msg)
puts "Hello, #{msg}"
end
end
class Employee < Person
def greet(msg)
super # Same as: super(msg)
super("another message")
end
end
没有参数或括号时,super
会接收方法的所有参数作为参数。否则,它会接收您传递给它的参数。
协变和逆变¶
继承在数组方面可能会有点棘手。我们在声明一个使用继承的对象数组时,必须小心。例如,考虑以下情况
class Foo
end
class Bar < Foo
end
foo_arr = [Bar.new] of Foo # => [#<Bar:0x10215bfe0>] : Array(Foo)
bar_arr = [Bar.new] # => [#<Bar:0x10215bfd0>] : Array(Bar)
bar_arr2 = [Foo.new] of Bar # compiler error
Foo 数组可以同时容纳 Foo 和 Bar,但 Bar 数组只能容纳 Bar 及其子类。
自动转换可能会让您在某些情况下感到困惑。例如,以下代码无法正常运行
class Foo
end
class Bar < Foo
end
class Test
@arr : Array(Foo)
def initialize
@arr = [Bar.new]
end
end
我们已将 @arr
声明为 Array(Foo)
类型,因此我们可能会认为可以开始在其中放入 Bar
。并非完全如此。在 initialize
中,[Bar.new]
表达式的类型始终为 Array(Bar)
。而 Array(Bar)
无法 赋值给 Array(Foo)
实例变量。
正确的做法是什么?更改表达式以使其具有正确的类型:Array(Foo)
(参见上面的示例)。
class Foo
end
class Bar < Foo
end
class Test
@arr : Array(Foo)
def initialize
@arr = [Bar.new] of Foo
end
end
这仅仅是一个类型(Array)和一个操作(赋值),上述逻辑将在其他类型和赋值中以不同的方式应用,总的来说,协变和逆变 没有得到完全支持。