クラスのスーパークラスはモジュール

お久しぶりです。さのです。
お客様先に出たおかげで、ソースコードがコピーできなくてブログ書けないとです。
言い訳です。

最近、がっつりRubyの勉強にはまってます。
Rubyの資格の勉強をしつつ、Railsでアプリ作りつつ、本を読んでいます。
今読んでる本がこちらです。

オライリー社のメタプログラミングRuby
https://www.oreilly.co.jp/books/9784873117430/

これがめっちゃ面白い!
まだ1章の途中ですが、すでにわくわくしています。
中でも今回気になったのが、RubyのClassの親クラスはModuleということ。

Class.superClass
=> Module

Moduleって何

Moduleって何かと簡単に説明しますと、メソッドを格納しておける入れ物みたいなものです。
こんな感じに。

module Greetable
  def say_morning
    "Good morning!"
  end

  def say_evening
    "Good evening!"
  end
end

どうやって使うかというと、includeを使ってクラスの中に埋め込みます。

class Sano
include Greetable
end

これで根暗なさのも挨拶ができるようになります。

sano = Sano.new
sano.say_morning
=> "Good morning!"
sano.say_evening
=> "Good evening!"

Moduleは使いまわせますので、同じメソッドを複数個所に記述する必要がまったく無くなる優れものです。
この機能はMix-inと呼ばれ、Rubyの特徴のひとつです。
さらに、同一クラスにModuleを複数Mix-inすることもできます。

module Refusable
  def refuse
    "No thank you"
  end
end

class Sano
  include Greetable
  include Refusable
end

sano = Sano.new
sano.say_morning
=>"Good morning!"
sano.refuse
=>"No thank you"

これで気弱なさのも挨拶しつつ、先輩の無茶振りを拒否することができるようになりました。
だんだん成長していきますね。
ちなみにメソッド名がかぶった場合、あとからincludeしたものでオーバーライドされます。

ここで最初のコードを再度確認してみましょう。

Class.superClass
=>Module

本によると、

ClassModuleに下記のメソッドを追加したもの
[:allocate, :new, :superclass]

だそうです。
:methods()でClassで定義されているメソッドが分かるので、それで確認ができます。
:methods()は引数にfalseを渡すことで継承されているメソッドを除外して表示します。

Class.methods(fase)
[:allocate, :new, :superclass]

つまり、newできないだけでModuleとClassは同じものということになります。
ここからわくわくポイントですが、

ってことはMix-inって継承と一緒じゃん

って思いませんか?
だって、メソッドを再利用できるんですから。
そして、

複数Mix-inって多重継承じゃん

ってことになりますよね。
Rubyにも当然Classの継承はありますが、多重継承はできないようになっています。
なので私は混乱しました。
おかしいですよね、Classの多重継承は禁止なのに、その親であるModuleの複数Mix-inはOKなんて。

そもそもなんで多重継承できないの?

多重継承を禁止している理由は2つです。

・親クラスに同じメソッド名があった場合、どれをオーバーライドすればいいかわからなくなる
・ひし形継承時に同一の親のインスタンスが複数作られる

1個目はいいでしょう。
それぐらいなんとかなる気がしますが、親1のメソッドと親2のメソッドがかぶったらどっちが優先されるとか、確かに覚えたくないです。
2個目はなんでしょうか。
ひし形継承を図で説明すると以下のようになります。

f:id:taiki_sano:20160312123209p:plain

クラスのインスタンスを作成するとします。
インスタンス作成時に親クラスのインスタンスも作成されるので、親3は2個インスタンスが作成されてどちらを参照すればいいかわからないというわけです。
これは確かにだめそうですね。

複数Mix-inは大丈夫なの?

Mix-inでは後からincludeされたものでオーバーライドすると明確に決まっています。
だから1個目の問題は解決でしょう。
2個目の問題に関してもModuleはnewできないClassなので問題ないです。

大丈夫じゃん!複数Mix-in!

なるほどnewできなければ多重継承もOKなんですねぇ。  

newできないClassってJavaにもありませんでしたっけ?

そう、interfaceです。
そしてそして、そういえばJava8からデフォルト実装といかいう謎の機能ありましたよね。

デフォルト実装が何かというと、interfaceに最初から中身のあるメソッドを記述することができる機能です。
つまり、以下のように書くことで

public interface GreetingInterface{
  // デフォルト実装
  default String sayMorning(){
    return "Good morning!";
  }
  default String sayEvening(){
    return "Good evening!";
  }
}
public interface RefuseInterface{
    default String refuse(){
      return "No thank you";
    }
}
class Sano implements GreentingInterface, RefuseInterface{
}

class Main{
  public static void main(String[] args){
    System.out.println(new Sano.sayMorning());
    // => Good morning!
    System.out.println(new Sano.refuse());
    // => No thank you
  }
}
一緒や!Mix-inと!

まてまて落ち着け、interfaceは型も継承するやん、Moduleは型継承なんて。。。

はっ!Rubyには型がねえ!

誤解があるといけないのできっちり書くと、Rubyの変数の型は実行時に決定されるのでコーディング時に意識する必要が無いのです。

結論:デフォルト実装=Mix-in

つながりましたね。
Ruby、噛めば噛むほど面白いですよー。