Rubyでオブジェクト指向プログラミングを学んでいると、「インスタンス変数(@)」と「クラス変数(@@)」という似たような用語が出てきて混乱することはありませんか?
特にクラス変数は、その特性を正しく理解していないと、継承関係にあるクラス間でのデータ共有で予期せぬトラブルを引き起こす原因になります。
この記事では、Rubyにおけるクラス変数の基本的な使い方から、インスタンス変数との違い、そしてベテランのRubyistたちが「クラス変数はなるべく使うな」と言う理由とその代替案まで、現在の開発トレンドに合わせて徹底解説します。
![]() 執筆者:マヒロ |
|
- OS:Windows 11 / macOS Sequoia
- IDE:Visual Studio / VS Code / IntelliJ IDEA
- その他:Chrome DevTools / 各言語最新安定版
※本メディアでは、上記環境にてコードの動作と情報の正確性を検証済みです。
Rubyのクラス変数とは?基本の書き方と特徴
クラス変数とは、その名の通り「クラスそのもの」に紐付く変数のことです。
変数名の先頭にアットマークを2つ(@@)付けることで定義できます。
最大の特徴は、「そのクラスのすべてのインスタンスで値を共有する」という点です。
インスタンスごとに別々の値を持つ「インスタンス変数」とは対照的です。
クラス変数の宣言と初期化
クラス変数は、クラス定義の中でメソッドの外側に記述して初期化するのが一般的です。
class Product
# クラス変数の定義と初期化
@@count = 0
def initialize(name)
@name = name
# インスタンスが作られるたびにクラス変数をカウントアップ
@@count += 1
end
def show_info
puts "商品名: #{@name}"
puts "これまでに作られたProductの総数: #{@@count}"
end
end
# インスタンス化
apple = Product.new("Apple")
apple.show_info
orange = Product.new("Orange")
orange.show_info
# 別のインスタンスからも共有された値が見える
apple.show_info
実行結果
商品名: Apple
これまでに作られたProductの総数: 1
商品名: Orange
これまでに作られたProductの総数: 2
商品名: Apple
これまでに作られたProductの総数: 2
上記のコードでは、@@count がクラス変数です。
Product.new で新しいインスタンス(apple や orange)を作成するたびに initialize メソッドが呼ばれ、@@count が1ずつ増えていきます。
最後に apple.show_info を再度呼び出すと、総数が「2」になっていることから、すべてのインスタンスで一つの @@count 変数を共有していることがわかります。
クラス変数とインスタンス変数の違いまとめ
「結局、どっちを使えばいいの?」と迷わないように、それぞれの特徴を表で整理しました。
| 特徴 | クラス変数 (@@) | インスタンス変数 (@) |
|---|---|---|
| 書き方 | @@name |
@name |
| 所属 | クラス自体 | 生成されたインスタンス(個体) |
| 値の共有 | 全インスタンスで共有される | インスタンスごとに独立している |
| 継承 | 子クラスとも共有される | 共有されない |
| 主な用途 | 全体設定、インスタンスの総数管理など | 個別の名前、年齢、状態など |
個々のオブジェクトの状態(名前や価格など)を管理したい場合はインスタンス変数を、クラス全体で共通の情報(定数に近い設定値や集計データなど)を管理したい場合はクラス変数を使います。
ただし、クラス変数には「定数(大文字で始める変数)」とも異なる、厄介な性質があります。
なぜ「クラス変数は使うな」と言われるのか?継承時の挙動
Rubyの学習を進めると、「クラス変数は非推奨」「クラスインスタンス変数を使え」といった意見を目にすることがあります。
その最大の理由は、クラス変数は「継承したサブクラス(子クラス)」とも値を共有してしまうため、予期せぬ副作用が起きやすいからです。
継承先で値が書き換わるトラブルの例
以下のコードを見てみましょう。
親クラスのクラス変数を、子クラスで変更してしまった場合どうなるでしょうか。
class Parent
@@message = "Parentのメッセージ"
def self.show_message
puts @@message
end
end
class Child < Parent
# 子クラスでクラス変数を上書き
@@message = "Childで書き換えました"
end
# 親クラスのメソッドを呼ぶ
Parent.show_message
実行結果
Childで書き換えました
Parent クラスで定義したはずの @@message が、子クラス Child の定義によって書き換えられてしまいました。
クラス変数は「継承ツリー全体」で共有されるため、子クラスでの変更が親クラスや他の兄弟クラスにまで影響を及ぼしてしまいます。
大規模なアプリケーションでこれが起きると、バグの原因特定が非常に困難になります。
これが、Rubyコミュニティでクラス変数の使用が慎重になる主な理由です。
代替案としての「クラスインスタンス変数」
クラス変数の「継承による副作用」を避けつつ、クラスレベルでデータを保持したい場合は、「クラスインスタンス変数」を使用するのがベストプラクティスです。
名前がややこしいですが、仕組みはシンプルです。
クラスインスタンス変数とは、「クラス定義の直下(メソッドの外)に書いた @変数」のことを指します。
クラスインスタンス変数の使い方
class Parent
# クラスインスタンス変数(@が1つ)
@message = "Parentのメッセージ"
def self.show_message
# クラスメソッド内からアクセス
puts @message
end
end
class Child < Parent
# 子クラスで同名の変数を定義
@message = "Childのメッセージ"
end
# それぞれのクラスの値を表示
Parent.show_message
Child.show_message
実行結果
Parentのメッセージ
Childのメッセージ
今度は Parent のメッセージが書き換わっていません。
クラスインスタンス変数は、「そのクラスオブジェクト自身」に紐付くインスタンス変数であるため、継承関係にあっても値は共有されません。
これにより、親子クラスで独立してクラスレベルのデータを管理できるようになります。
クラス変数へのアクセスとアクセサメソッド
クラス変数は、デフォルトではクラスの外部から直接参照することができません。
外部からアクセスしたい場合は、専用のメソッド(クラスメソッド)を定義する必要があります。
attr_accessor は使えない?
インスタンス変数の場合は attr_accessor :name のようにして外部公開できますが、クラス変数にはそのような便利なショートカットは標準では用意されていません(クラスインスタンス変数の場合は class << self 内で attr_accessor が使えます)。
クラス変数を操作する場合は、以下のように手動でメソッドを定義します。
class Config
@@max_users = 100
# クラス変数を取得するクラスメソッド
def self.max_users
@@max_users
end
# クラス変数を変更するクラスメソッド
def self.max_users=(value)
@@max_users = value
end
end
# 外部からアクセス
puts Config.max_users
Config.max_users = 200
puts Config.max_users
実行結果
100
200
self.メソッド名 でクラスメソッドを定義し、その中で @@変数 を返したり代入したりすることで、外部からの操作を可能にしています。
Rubyのスキルを活かして年収を上げる方法
以上、Rubyのクラス変数や、インスタンス変数との違いなどについて解説してきました。
Rubyエンジニアの年収や単価は高い傾向にあるため、転職によって数十万円の年収アップはザラで、100万円以上年収が上がることも珍しくありません。
なお、転職によって年収を上げたい場合は、エンジニア専門の転職エージェントサービスを利用するのが最適です。
転職エージェントも副業エージェントも、登録・利用は完全無料なので、どんな求人や副業案件があるのか気になる方は、気軽に利用してみるとよいでしょう。
| 年収アップにこだわりたい方 (平均アップ額138万円の実績) | テックゴー |
| 未経験・経験者問わず幅広く探したい方 | ユニゾンキャリア |
| 業界に精通した担当者に相談したい方 | キッカケエージェント |
| ゲーム業界への転職を志望する方 | ファミキャリ |
| エンジニア未経験からキャリアを築く方 | イーチキャリア |



