クラスメソッドとスタティックメソッドについて


Python、Ruby、Javaのクラスメソッドとスタティックメソッドについてまとめました。

Pythonにはクラスメソッドとスタティックメソッドがあり、Rubyにはクラスメソッドはありますがスタティックメソッドはなく、Javaにはクラスメソッドはないですがスタティックメソッドはあります。

まとめると以下のようになります。

|            | Python | Ruby | Java |
|:----------:|:------:|:----:|:----:|
|クラスメソッド|    ◯   |  ◯    |  ✕  |
|スタティックメソッド|    ◯   |  ✕    |  ◯  |

言語毎の動作を見ていきます。

##Python

Pythonにはクラスメソッドとスタティックメソッドがあります。

クラスメソッド、スタティックメソッドともに、インスタンス変数にはアクセスできません。

```py:
class ClassSample:
  class_var = "hoge"

  @classmethod
  def class_method(cls):
    print "%s, class_var: %s" % (cls, cls.class_var)

  @staticmethod
  def static_method():
    print "%s, class_var: %s" % (ClassSample, ClassSample.class_var)

class SubclassSample(ClassSample):
  class_var = "foo"

ClassSample.class_method() # -> __main__.ClassSample, class_var: hoge
ClassSample.static_method() # -> __main__.ClassSample, class_var: hoge
SubclassSample.class_method() # -> __main__.SubclassSample, class_var: foo
SubclassSample.static_method() # -> __main__.ClassSample, class_var: hoge
```

インスタンスメソッドの場合は第一引数にインスタンスが渡されますが、クラスメソッドの場合は第一引数にクラスが渡されます。
そのため、クラスメソッドは渡ってきたクラスを使ってクラス変数にアクセスすることができます。

スタティックメソッドはクラスが渡されないため、クラス変数にアクセスするためにはクラスを宣言する必要があります。

継承時の動作は、クラスメソッドの場合は第一引数に子クラスが渡されるため、クラス変数は子クラスのクラス変数の値となります。
一方、スタティックメソッドの場合はクラスを宣言することでクラス変数にアクセスするため、宣言したクラスが親クラスか子クラスかによって値が変わります。

まとめると以下のようになります。

|                 |インスタンス変数へのアクセス|クラス変数へのアクセス|継承時のクラス変数の値|
|:---------------:|:---------------------:|:----------------:|:----------------:|
|クラスメソッド     |不可                    |可                 |子のクラス変数の値   |
|スタティックメソッド|不可                    |自クラスを宣言すれば可|宣言したクラスに依存 |


##Ruby

Rubyはクラスメソッドがあります。

Pythonとは異なり、インスタンス変数へのアクセスが可能です。
ただし、クラススコープで宣言されたインスタンス変数へのみアクセスが可能であり、クラススコープではない場所で宣言されたインスタンス変数へのアクセスはできません。

また、クラス変数のスコープが広いため、継承先の子クラスでクラス変数を書き換えた場合、それが親クラスにも反映されます。

```ruby:
class ClassSample
  @instance_var = "hoge"
  @@class_var = "moge"

  def self.class_method
    puts "#{self}, instance_var: #{@instance_var}, class_var: #{@@class_var}"
  end
end

class SubclassSample < ClassSample
  @instance_var = "foo"
  @@class_var = "bar"
end

ClassSample.class_method # -> ClassSample, instance_var: hoge, class_var: bar
SubclassSample.class_method # -> SubclassSample, instance_var: foo, class_var: bar
```

ClassSample.class_methodのクラス変数の出力が、mogeではなくbarになっています。
これは継承先でクラス変数を書き換えたためです。

まとめると以下のようになります。

|           | インスタンス変数へのアクセス | クラス変数へのアクセス | 継承時のクラス変数の値|
|:---------:|:-----------------------:|:------------------:|:-----------------:|
|クラスメソッド|クラススコープで宣言された場合は可|    可           |子のクラス変数の値    |


## Java

Javaにはスタティックメソッドがあります。

インスタンス変数へのアクセスはできませんが、クラス変数へのアクセスはできます。
Javaのクラス変数は、静的な変数としてクラスと紐付いているので継承時の動作が異なります。

```java:
class ClassSample {
    static String classVar = "hoge";
    public static void staticMethod() {
        System.out.println("classVar: " + classVar);
    }
}

class SubclassSample extends ClassSample {
    static String classVar = "foo";
}

class Test {
    public static void main(String[] args) {
        ClassSample.staticMethod(); // -> classVar: hoge
        SubclassSample.staticMethod(); // -> classVar: hoge
    }
}
```

継承先の子クラスでクラス変数を書き換えても、親クラスにも子クラスにも反映はされません。

まとめると以下のようになります。

|           | インスタンス変数へのアクセス | クラス変数へのアクセス | 継承時のクラス変数の値|
|:---------:|:-----------------------:|:------------------:|:-----------------:|
|スタティックメソッド|不可|    可           |親のクラス変数の値    |