コンテンツにスキップ

継承

階層ルートである`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

これは、1つの型(Array)と1つの操作(代入)についてのみです。上記のロジックは、他の型と代入については異なる方法で適用されます。一般に、共変性と反変性は完全にはサポートされていません。