他の言語と少し異なるRubyのメソッドの公開レベル

#はじめに
以前、Javaを少しかじっていたのですが、JavaとRubyでは公開レベルの指定についての考え方が少し異なっていたので、自分なりにまとめて公開します。
#public private protectedメソッド
Rubyには、

**public**
**private**
**protected**

の3つのメソッドを使うことで、公開レベルを指定することができます。
これらは、他の言語を知っている方にとって、馴染みあるキーワードだと思う人も多いと思いますが、Rubyのは、少し考え方が異なっています。

#publicメソッド
クラスの内外かかわらず、自由にアクセスすることができます。
わざわざ指定をしなくても、インスタンスメソッドはデフォルトでpublicメソッドになっています。

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

  #何も指定されていない -> publicメソッド
  def self_introduction
    puts "私の名前は#{@name}です!"
  end
end

user = User.new('Tom')
#クラスの外でも呼び出せる。
user.self_introduction  #結果:私の名前はTomです！

```

#privateメソッド
クラスの中でのみアクセス可能なメソッドです。先程の、self_introduceメソッドをprivateにしてみます。

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

　#これより下で定義されたメソッド全てがprivateメソッドになる。
  private

  #何も指定されていない -> publicメソッド
  def self_introduction
    puts "私の名前は#{@name}です!"
  end
end

user = User.new('Tom')
#クラスの外からは呼び出せない！
user.self_introduction  
#結果:`<main>': private method `self_introduction' called for<User:0x00007faef2843318 @name="Tom"> (NoMethodError)
```
また、privateキーワードでprivateになるのはインスタンスメソッドのみで、クラスメソッドはprivateになりません。

ちなみに、Ruby2.7以前では、privateメソッドは、クラス内でもレシーバーをつけて呼び出すことができなかったのですが、```self.```くらいはつけてもいいじゃんという用途からRuby2.7では改善され、```self.```をつけてもプライベートメソッドを呼び出せるようになりました。。

#####privateメソッドの他の言語と違う特徴
Rubyのprivateメソッドでは、__子クラス（サブクラス）からでも呼び出すことができます。__

```private.rb
class Person
  #これより下はprivateメソッド
  private
  
  def name
    'Tom'
  end
end

class User < Person
  def self_introduce
    #nameはPersonクラスからの継承
    puts "私の名前は#{name}です！"
  end
end

user = User.new
#親クラスのprivateメソッドを指定しているが、エラーにならない
user.self_introduce #私の名前はTomです！

```

#protectedメソッド
privateメソッドと似ており、クラスの外からは呼び出すことはできません。しかし、protectメソッドは、他のインスタンスからでも、そのインスタンスが同じクラスであれば呼び出すことができます。

privateメソッドと比べてみましょう。

```private.rb
class User
  attr_reader 'name'

  def initialize(name,study_time)
    @name = name
    @study_time = study_time
  end
  
  #opponent.study_time -> privateメソッド
  def longer_than?(opponent)
    @study_time > opponent.study_time
  end
  
  #これより下はprivateメソッド
  private

  def study_time
    @study_time
  end
end

tom = User.new('Tom',120)
john = User.new('John',100)


#longer_thanメソッドは、johnインスタンスのprivateメソッドであるstudy_timeを呼び出そうとするために、エラーになる。
if tom.longer_than?(john)
  puts "私(#{tom.name})、は#{john.name}より長く勉強しました。"
else
    puts "参りました..."
end
#結果(エラー):`longer_than?': private method `study_time' called for #<User:0x00007fd9a1026ae8 @name="John", @study_time=100> (NoMethodError)
```

tomインスタンスからjohnインスタンスのprivateメソッドを呼び出そうとしたため、エラーになってしまいました。
しかし、他のインスタンスからでも同じクラスからなら呼び出すことのできるprotectedメソッドなら、呼び出すことができます。

```protected_rb
class User
  attr_reader 'name'

  def initialize(name,study_time)
    @name = name
    @study_time = study_time
  end
  
  #opponent.study_time = protectedメソッド
  def longer_than?(opponent)
    @study_time > opponent.study_time
  end
  
  #これより下はprotectedメソッド
  protected

  def study_time
    @study_time
  end
end

tom = User.new('Tom',120)
john = User.new('John',100)

#他のインスタンスから、study_timeメソッドを呼び出しているが、同じクラスなので呼び出せる。
if tom.longer_than?(john)
  puts "私(#{tom.name})、は#{john.name}より長く勉強しました。"
else
    puts "参りました..."
end
#結果:私(Tom)、はJohnより長く勉強しました。


#もちろん、クラスの外からは呼び出せない。
puts john.study_time
#`<main>': protected method `study_time' called for #<User:0x00007fad4214a9f0 @name="John", @study_time=100> (NoMethodError)

```

#まとめ
①```publicメソッド```
 ->　クラス内外問わずどこからでも呼び出せる。（デフォルト）

②```privateメソッド```
 ->クラスの外からは呼び出せない。

③```protectedメソッド```
 ->クラスの外からは呼び出せないが、同じインスタンス内or同じクラスの他インスタンスであれば呼び出せる。