クラスにフィールドを持たせる

生田 陸人
LuaGate エンジニア / 現役エンジニア
編集 LuaGate編集部

このレッスンで分かること

  • フィールドはクラスの中に書く変数で、インスタンスごとに値を保持します
  • アクセス修飾子 型 名前; の形で宣言し、obj.field でアクセスします
  • 最小例は class Book { String title; } ... Book b = new Book(); b.title = "Java";

クラスにフィールドを持たせる とは

クラスにフィールドを持たせて、オブジェクトが「データ」を抱えられるようにする方法を学びます。本レッスンでは、クラスにフィールドを持たせる の基本から実際の使いどころまでを整理し、現場で迷わず使える形に落とし込みます。

クラスにデータを持たせる ─ フィールドの正体

前のレッスンで class という箱を作る方法を学びました。ただしあの段階の class は、まだ「空き箱」に近い存在です。中身が空っぽのままでは Book クラスも User クラスも、ただの名前だけの飾りになってしまいます。

ここで登場するのが フィールド (field) です。フィールドとは、クラスの中に書く変数のことで、そのクラスから作られたオブジェクトが「自分のデータ」として持ち続ける値を表します。Book クラスなら「題名」「著者」「ページ数」、User クラスなら「名前」「年齢」「メールアドレス」── こうした情報の置き場所がフィールドです。

フィールドは英語の field (畑、領域) から来た言葉で、データを耕しておく区画のようなイメージです。C# では fieldPython では attributeJavaScript では property と呼びますが、役割はほぼ同じだと思って大丈夫です。

フィールドが書ければ、class はようやく本来のチカラを発揮します。ひとつの設計図 (class) から、それぞれ違うデータを持った複数のオブジェクト (インスタンス) を量産できるようになるからです。たとえば Book クラスがあれば、b1 には『吾輩は猫である』、b2 には『Java 入門』、b3 には Effective Java ── と、同じ型の入れ物にそれぞれ違うタイトルを詰めることができます。

フィールドとは何か

フィールドの定義を一度きちんと整理しておきましょう。フィールドの特徴は次のとおりです。

  • クラスの中 (メソッドの外側) に書く変数
  • そのクラスから作られたオブジェクトごとに値を保持する (インスタンスフィールド)
  • もしくは、クラス全体で 1 個だけ共有する (静的フィールド、static 付き)
  • 型 (String int boolean など) を必ず付ける
  • アクセス修飾子 (public private protected) で見える範囲を制御できる

初心者がよく混乱するのが「メソッドの中で宣言した変数」と「フィールド」の違いです。メソッドの中で String name = "Java"; と書いた nameローカル変数 で、メソッドを抜けた瞬間に消えます。一方でクラス直下に書いたフィールドは、そのオブジェクトが生きている間ずっと値を覚え続けます。

例えるなら、ローカル変数は「会議室のホワイトボード」、フィールドは「自分の手帳」です。会議が終わればホワイトボードは消されますが、手帳に書いた内容はあとから何度でも読み返せます。

フィールドの宣言文法

フィールドの書き方はとてもシンプルです。「アクセス修飾子 + 型 + 名前 + (初期値) + ;」の順番で書きます。

Java

public class Book { public String title; // 題名 (String 型) public String author; // 著者 (String 型) public int pages; // ページ数 (int 型) public boolean isAvailable; // 貸出可能か (boolean 型) }

このコードは「Book というクラスには title author pages isAvailable という 4 つのフィールドがある」と宣言しているだけで、まだ実体 (オブジェクト) は作られていません。

初期値を直接書くこともできます。たとえば「初期状態は貸出可能」と決めておきたいなら、宣言と同時に値を渡せます。

Java

public class Book { public String title = "未設定"; public int pages = 0; public boolean isAvailable = true; }

初期値を書かなかった場合、Java は型ごとに 既定値 を自動で入れてくれます。

既定値
int long short byte0
double float0.0
booleanfalse
char
参照型 (String など)null

null のままアクセスすると NullPointerException が出るので、String 型のフィールドはなるべく初期化しておくのが安全です。

インスタンスフィールド vs 静的フィールド

フィールドには大きく分けて 2 種類があります。static が付くか付かないかで意味がガラッと変わるので、ここはしっかり押さえておきましょう。

Java

public class Book { public String title; // インスタンスフィールド public static int totalCount; // 静的フィールド (クラス全体で 1 個) }
  • インスタンスフィールド ── オブジェクトごとに別々の値を持つ。b1.titleb2.title は独立
  • 静的フィールド (クラスフィールド) ── クラス全体で 1 個だけ。Book.totalCount のようにクラス名から直接アクセスする

「世の中に存在する Book の冊数を全部数えたい」なら静的フィールドが便利ですし、「1 冊ごとの題名」を持ちたいならインスタンスフィールドの出番です。迷ったら 「個体差があるか?」 で判断してください。個体差があるならインスタンス、ないなら static です。

static を付け忘れて全インスタンスで共有させたかった値が個別管理になってしまった、というバグはあるあるです。逆に static を付けすぎて、本来は個別に持つべき題名がクラス全体で 1 個になってしまうのも危険です。

アクセス修飾子をやんわり紹介

フィールドの先頭に付く publicprivateアクセス修飾子 と呼びます。今は深掘りせず、雰囲気だけ掴んでおきましょう。

  • public ── どこからでも読み書きできる。手軽だが無防備
  • private ── そのクラスの内側からしか触れない。データを守る基本姿勢
  • protected ── 同じパッケージか、継承したサブクラスからアクセスできる
  • 修飾子なし (パッケージプライベート) ── 同じパッケージからのみアクセスできる

本格的な現場のコードでは、フィールドはほとんど private にして、getter / setter メソッド経由で操作するのが定石です。これを カプセル化 (encapsulation) と呼びますが、本レッスンでは「まずフィールドを置いてみる」段階なので public で十分です。次のレッスン以降で private への移行を扱います。

Book クラスとフィールドの関係を図にする

ここまでの話を、Book クラスを例にしたクラス図でまとめてみましょう。title author pages の 3 つのフィールドと、それを使うメソッド createBookTitle() の関係を視覚化します。

diagram (will load when visible)

図の関係を箇条書きで整理すると、次の通りです。

  • Book クラスはフィールド title author pages を持つ
  • Solution.createBookTitle()Book インスタンスを作成 → 値を設定 → 値を読み出す
  • SolutionBook を「使う」(依存) 関係 (UML の ..>)
  • フィールドの +public を示す UML 記号

実際にコードに落とすと、こんなイメージになります。

Java

class Book { public String title; } public class Solution { public static String createBookTitle() { Book book = new Book(); book.title = "Java 入門"; return book.title; } }

new Book()Book の実体を 1 つ作り、book.title に値を代入、最後に book.title を読み取って return で返しています。設計図 → 実体化 → フィールド書き込み → 読み出し という、オブジェクト指向の基本的な 4 ステップが詰まったコードです。

よくある間違い

フィールドを書き始めると、多くの初心者が同じ場所でつまずきます。代表的な落とし穴を 3 つ紹介します。

  • アクセス修飾子を書き忘れる ── 修飾子なしで String title; と書くと「パッケージプライベート」になります。最初のうちは public を明示的に書きましょう
  • static の付け間違い ── インスタンスフィールドにすべき題名に static を付けてしまうと、全 Book オブジェクトでタイトルが共有されてしまいます
  • 初期化を忘れて NullPointerException ── String title; のまま book.title.length() を呼ぶと null に対してメソッドを呼ぶことになり、実行時にクラッシュします

もうひとつ地味に多いミスが、フィールドとローカル変数の名前衝突 です。メソッドの引数に title と書きつつ、フィールドにも title がある場合、何もしないと「引数の title」が優先されてフィールドが書き換わらないという罠が発生します。これは将来 this.title = title; という書き方で解決しますが、今は「同じ名前を使わない」だけで十分です。

エラーメッセージで variable might not have been initialized と出たら、フィールドではなくローカル変数の初期化忘れがほとんどです。

この章のポイント

ここまでの要点 フィールドはクラスの中に書く変数。インスタンスフィールドはオブジェクトごと、static はクラス全体。null 参照のフィールドアクセスは NullPointerException

やってみよう

それでは、このレッスンの課題に挑戦しましょう。題材は 「引数で受け取った文字列を Book.title に書き込み、それを読み出して返す」 というものです。固定の文字列を即 return するのは禁止です。必ずフィールドを経由してください。

  1. 右側のエディタで Solution.createBookTitle(String title) を開く
  2. new Book() でインスタンスを作る
  3. book.title = title; のようにフィールドへ代入する
  4. return book.title; で読み出した値を返す

Book クラスはすでに static class Book { ... } として Solution の中に用意してあります。フィールド title author pages の宣言はそのままで構いません。

慣れてきたら book.authorbook.pages にも値を入れて、title + " / " + author のような形に describe() 風メソッドを書き足してみてください。次のレッスンで扱う インスタンスメソッド の予習になります。

次のレッスン

次は クラスにメソッドを定義する で、クラスにフィールドを持たせて、オブジェクトが「データ」を抱えられるようにする方法を学びます を学びます。

事前確認 — 進む前に次の 3 つができることを確認しましょう。

  1. フィールド の要点を自分の言葉で説明できる
  2. このレッスンの最小コード (または操作手順) を見ずに書ける
  3. 練習問題やクイズで間違えた箇所を読み直して理解した

理解度チェック (30 秒)

Q. フィールド とは何か、1 文で説明してください。

この章のポイント

A. 本文の「このレッスンで分かること」または冒頭の説明文を見直し、自分の言葉で要約できれば OK。詰まったら本レッスンの最初の H2 セクションを読み返してみましょう。

関連レッスン

要件

  1. createBookTitle の中で new Book() を使ってインスタンスを作ること
  2. book.title フィールドに引数 title を代入してから return すること (固定文字列の即時 return は不可)
  3. 戻り値はそのインスタンスの title フィールドの値であること

入出力例

test-cases.txt

createBookTitle("Java 入門")"Java 入門" createBookTitle("Effective Java")"Effective Java" createBookTitle("")""

ヒント

main.java
main.java
学習モード

メモ

クラスにフィールドを持たせる

⌘S で保存