Ruby クラス、インスタンス、継承

動作環境はMacです。

##クラス##


クラスとは車でいえば設計図のこと。
インスタンスは、設計図を元に作られる自動車そのもの。
ゆえにクラスの要素はもつが、個々のインスタンスでの特徴は違う。

**オリジナルクラスの作成**


*構文*

**class**　クラス名
**end**

**実践＊＊

```car.rb

class Car #Carクラスを作成。頭文字は大文字
  def hello　#メソッドの定義
    puts "Hello!"
  end
end

car = Car.new　　#newメソッドでインスタンスを生成し、変数carに格納
car.hello　#インスタンス変数の呼び出し
```

```cae.rb
class Car
  def initialize(name) #インスタンスが作成(new)されると同時に実行
　　　　　　　　puts "初期化されました"
    @name = name　#インスタンス変数に引数nameの値を格納
  end

  def hello
    puts "Hello! I am #{@name}"　#インスタンス変数はインスタンスの中であればどこからでも参照可能
  end
end

car = Car.new("Kitt")　#initializeされ、引数”Kitt”がnameに渡される
car.hello

karr = Car.new("Karr")
car.hello
```

```car.rb
初期化されました　#newされたタイミングで出力される
Hello! I am Kitt
初期化されました
Hello! I am Kitt
```

クラスに所有されるインスタンスであればいくらでも呼び出すことができる。

**アクセサ(インスタンス)メソッド**

ゲッター、セッターとも呼ばれる。
インスタンス変数はクラスの外部から呼び出すことができないため、
呼び出せるように設定するメソッド。

```accessor.rb

class Car
  def initialize(name)
    puts "初期化されました"
    @name = name
  end

  def hello
    puts "Hello! I am #{@name}"
  end
end

car = Car.new("Kitt")
car.hello

car.@name #クラス外部からインスタンス変を呼び出す
```
出力結果

```
ruby accessor.rb
accessor.rb:15: syntax error, unexpected tIVAR, expecting '('
car.@name
         ^
```
エラーになる。

```accessor.rb

class Car
  def initialize(name)
    puts "初期化されました"
    @name = name
  end

  def hello
    puts "Hello! I am #{@name}"
  end

  def name #外部から読み取り可能なメソッドを定義
    @name
  end
end

car = Car.new("Kitt")
car.hello

puts car.name　#呼び出し
```

出力結果

```
ruby accessor.rb
初期化されました
Hello! I am Kitt
Kitt #呼び出された
```

```accessor.rb

class Car
  def initialize(name)
    puts "初期化されました"
    @name = name
  end

  def hello
    puts "Hello! I am #{@name}"
  end

  def name
    @name
  end

  def name=(value)　#nameのを書き換えるメソッド。=も含まれる。スペースは開けない
    @name = value　#インスタンス変数にvalueを代入
  end

end

car = Car.new("Kitt")
car.hello

puts car.name
car.name = "sisido"　#nameを書き換え
puts car.name
```

出力結果

```
初期化されました
Hello! I am Kitt
Kitt
sisido #書き換わった！
```

**アクセサメソッド2**

**attr_accessor**

インスタンスメソッドを簡単に書き込みや読み込みができるようにするためのメソッド

```accessor.rb
def name
  @name
end

def name=(value)
  @name = value
end

#この記述を簡単に書き換える
```
↓

```accessor.rb
class Car

  attr_accessor :name #この箇所にアクセサメソッドを記述

  def initialize(name)
    puts "初期化されました"
    @name = name
  end

  def hello
    puts "Hello! I am #{@name}"
  end


  #def name
    #@name
  #end

  #def name=(value)
    #@name = value
  #end

end

car = Car.new("Kitt")
car.hello

puts car.name
car.name = "sisido"
puts car.name
```
出力結果

```irb
初期化されました
Hello! I am Kitt
Kitt
sisido
```

読み取り専用メソッド

```accessor.rb
class Car

  #attr_accessor :name
  attr_reader :name #読み取り専用のメソッド

  def initialize(name)
    #puts "初期化されました"
    @name = name
  end

  def hello
    puts "Hello! I am #{@name}"
  end

  #def name
    #@name
  #end

  #def name=(value)
    #@name = value
  #end

end

car = Car.new("Kitt")
#car.hello
#car.@name NG

puts car.name
#car.@name = "sisido" NG
#car.name = "sisido"
puts car.name
```

```irb
Kitt
Kitt
```

書き込み専用のメソッド

```attr_accessor.rb

class Car

  #attr_accessor :name
  #attr_reader :name
  attr_writer :name　#書き込み専用メソッドの記述

  def initialize(name)
    #puts "初期化されました"
    @name = name
  end

  def hello
    puts "Hello! I am #{@name}"
  end

  #def name
    #@name
  #end

  #def name=(value)
    #@name = value
  #end

end

car = Car.new("Kitt")
#car.hello
#car.@name NG

#puts car.name
#car.@name = "sisido" NG
car.name = "sisido" 
#puts car.name
```

**クラス変数**

クラス自体に値を保持することができる変数

**実践**

```class_val.rb

class Car
  @@counter = 0 #クラス変数はここに記述
  def initialize(name)
    @name = name
    @@counter += 1　#インスタンスが作成されるごとにインクリメントされる
  end

  def hello
    puts "Hello! I am #{@name}.#{@@counter} instanse(s)."#この中にインスタンスの作成回数を記録
  end
end

kitt = Car.new("Kitt")
kitt.hello

karr = Car.new("Karr")
karr.hello

imawano = Car.new("imawano")
imawano.hello
```

出力結果

```
Hello! I am Kitt.1 instanse(s).
Hello! I am Karr.2 instanse(s).
Hello! I am imawano.3 instanse(s).
```

**クラスメソッド**

クラスから直接呼び出すことができるメソッド

**実践**

```class_val.rb

class Car
  @@counter = 0
  def initialize(name)
    @name = name
    @@counter += 1
  end

  def hello
    puts "Hello! I am #{@name}.#{@@counter} instanse(s)."
  end

  def self.info #クラスメソッドの作成　selfをつける
    puts "#{@@counter} instance(s)"
  end
end

#kitt = Car.new("Kitt")
#kitt.hello

#karr = Car.new("Karr")
#karr.hello

#imawano = Car.new("imawano")
#imawano.hello

Car.info　#クラスメソッドの呼び出し
```
出力結果

```
0 instance(s) #インスタンスは作成していないため0
```

インスタンスが作成された直後にクラスメソッドを呼び出す

```class_val.rb
class Car
  @@counter = 0
  def initialize(name)
    @name = name
    @@counter += 1
  end

  def hello
    puts "Hello! I am #{@name}.#{@@counter} instanse(s)."
  end

  def self.info
    puts "#{@@counter} instance(s)"
  end
end

kitt = Car.new("Kitt")
#kitt.hello
Car.info

karr = Car.new("Karr")
#karr.hello
Car.info

imawano = Car.new("imawano")
#imawano.hello
Car.info
```

出力結果

```

1 instance(s)
2 instance(s)
3 instance(s)

```

１つのクラスをインスタンスが共通して使用していることがわかる。

**クラスと定数**

```class_const.rb
class Car
  REGION = "USA" #定数の定義
  @@counter = 0
  def initialize(name)
    @name = name
    @@counter += 1
  end

  def hello
    puts "Hello! I am #{@name}.#{@@counter} instanse(s)."
  end

  def self.info
    puts "#{@@counter} instance(s). REGION: #{REGION}"　##クラスメソッド内で定数を使用
  end
end

kitt = Car.new("Kitt")
#kitt.hello
Car.info

karr = Car.new("Karr")
#karr.hello
Car.info

imawano = Car.new("imawano")
#imawano.hello
Car.info

puts Car::REGION　#定数の呼び出し
```

定数の作成や呼び出しはクラス変数やインスタンス変数と違うので注意

＊＊クラスの継承**

スーパークラス(親クラス)の振る舞いをサブクラス（子クラス）でも使用することができる

**実践＊＊

```sub_class.rb
class User
  def initialize(name)
    @name = name
  end

  def hello
    puts "Hello! I am #{@name}."
  end
end

class AdminUser < User #Userクラスの継承
  def admin_hello #AdminUserクラス内でメソッドを定義
    puts "Hello! I am #{@name} from AdminUser."
  end
end

iwamano = User.new("iwamano")
iwamano.hello

sansiro = AdminUser.new("sansiro")　
sansiro.hello　#AdminUserクラスのでUserクラスのメソッドを使用
sansiro.admin_hello　#AdminUserクラス内のメソッドを使用
```

出力結果

```
Hello! I am iwamano.
Hello! I am sansiro.
Hello! I am sansiro from AdminUser.
```

Userクラス（親クラス）からAdminUserクラスのメソッドを呼び出すことはできない

```sub_class.rb

iwamano = User.new("iwamano")
iwamano.hello
iwawano.admin_hello
```

出力結果

```
Hello! I am iwamano.
sub_class.rb:19:in `<main>': undefined method `admin_hello' for #<User:0x007fbc6a19f4c8 @name="iwamano"> (NoMethodError) #エラーが返る
```

**オーバーライド**

親クラスの持つ振る舞いを子クラスで上書きや追加をすることができる機能

**実践**

```sub_class.rb

class User
  def initialize(name)
    @name = name
  end

  def hello
    puts "Hello! I am #{@name}."
  end
end

class AdminUser < User
  def admin_hello
    puts "Hello! I am #{@name} from AdminUser."
  end

  def hello
    puts "Admin!" #親クラスのhelloメソッドに上書き
  end
end

iwamano = User.new("iwamano")
iwamano.hello #子クラスのhelloメソッドの呼び出し
#iwamano.admin_hello

sansiro = AdminUser.new("sansiro")
sansiro.hello
sansiro.admin_hello
```

出力結果

```
Hello! I am iwamano.
Admin!　#親クラスのhelloメソッドは呼び出されず、子クラスのhelloメソッドが呼び出されている
Hello! I am sansiro from AdminUser.
```

オーバーライドは親クラスの修正を加えることなく子クラスに同名のメソッドを記述することで、上書きや追加をすることができる。

**クラスの確認**

クラス名.superclassで確認することができる。

例)Integerクラス

```irb
irb(main):002:0> Integer.superclass
=> Numeric
irb(main):003:0> Numeric.superclass
=> Object
irb(main):004:0> Object.superclass
=> BasicObject
irb(main):006:0> BasicObject.superclass
=> nil #BasicObjectクラスが大元の親クラスとなるため、nilが返る
```