跳至內容

繼承

除了階層根節點 Object 之外,每個類別都繼承自另一個類別(其父類別)。如果您沒有指定,類別預設為 Reference,結構預設為 Struct

類別會繼承父類別的所有實例變數以及所有實例方法和類別方法,包括其建構子 (newinitialize)。

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"

如果類別定義了 newinitialize,則不會繼承其父類別的建構子

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