繼承¶
除了階層根節點 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) 和一個操作 (賦值),上述邏輯將以不同的方式應用於其他型別和賦值,一般而言,共變與逆變 並不完全支援。