this キーワード

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

this キーワード とは

this は「このオブジェクト自身」を指す参照です。フィールドと引数の名前衝突を解決したり、メソッドチェーンを書いたり、別のコンストラクタを呼んだりするために使います。

this キーワード ─ 自分自身を指す矢印

クラスとオブジェクトを学んでいくと、ほぼ必ず登場するのが this というキーワードです。Java だけでなく C++ C# Python (こちらは self) JavaScript など、ほとんどのオブジェクト指向言語に存在する基本概念です。

this の意味を一言で言うと「いま動いているメソッドが属している、このオブジェクト自身」です。インスタンスメソッドの中から自分のフィールドや別メソッドへ確実にアクセスするための矢印、とイメージしてください。

名前は素っ気ないですが、役割を知ると一気に Java らしいコードが書けるようになります。このレッスンでは this の 4 つの使い方を、実例を交えて丁寧に解きほぐしていきます。

this とは何か

Book というクラスを考えてみましょう。new Book(...) でインスタンスを作ると、メモリの中に Book のインスタンスが 1 つ生まれます。さらに new Book(...) をもう一度呼ぶと、別の場所にもう 1 つ Book が生まれます。同じ Book クラスから作られているのに、それぞれは独立した「箱」です。

ここで、title を取り出すメソッド getTitle() を呼んだとき、Java はどちらの箱の title を返すべきか判断しなければなりません。その判断材料が this です。book1.getTitle() を呼んだ瞬間、getTitle の中の thisbook1 を指します。book2.getTitle() を呼べば thisbook2 を指します。

diagram (will load when visible)

つまり this は「いま自分が呼ばれた、その対象のインスタンス」を自動的に受け取る、目に見えない引数のようなものです。Java がメソッド呼び出しのたびにそっと渡してくれている、と覚えておきましょう。

static メソッドの中では this は使えません。static はインスタンスに属さず、クラスそのものに属するメソッドなので、「自分自身のインスタンス」が存在しないからです。これは初学者が必ずどこかでハマるポイントです。

使い方 1 ─ フィールドと引数の名前衝突を解決する

一番よく使うのが、コンストラクタや setter でフィールド名と引数名が同じになるケースです。例を見てみましょう。

Java

public class Book { private String title; private int price; public Book(String title, int price) { this.title = title; this.price = price; } }

Book(String title, int price) の中には、引数の title とフィールドの title という同名のものが 2 つ存在します。何もしなければ Java はより近いスコープ、つまり引数の方を優先します。フィールドに代入したいときは this.title と書くことで「フィールドの方の title」を明示できます。

引数名をわざわざ t p のように短縮する流派もありますが、現代 Java では this.title = title; のように同名にして this で区別する書き方が主流です。読み手にとっても「この引数はこのフィールドに入る」というペアが一目でわかります。

もし this を付け忘れて title = title; と書いてしまうと、引数を引数自身に代入するだけで、フィールドには何も入りません。コンパイルは通るのに値が入っていない、というバグが起きるので注意してください。IntelliJVS Code の警告で気づけることが多いですが、自分で書くときは「フィールド代入には this」と体に染み込ませましょう。

使い方 2 ─ メソッドチェーン (this を return)

StringBuilderappend().append().append() のように、メソッドを . でつなげて書くスタイルを「メソッドチェーン」と呼びます。これを自作クラスで実現するには、メソッドの最後で return this; するのが定番です。

Java

public class Book { private String title; private int price; public Book setTitle(String title) { this.title = title; return this; } public Book setPrice(int price) { this.price = price; return this; } } // 呼び出し側 Book b = new Book().setTitle("Java入門").setPrice(2000);

setTitlesetPriceBook を返すので、. でどんどんつなげていけます。Builder パターンや Stream API の filter().map().collect() も、この return this; のテクニックがベースです。

メソッドチェーンは便利ですが、やりすぎると 1 行が長くなって読みづらくなります。3〜5 段までを目安に、改行とインデントで整えるのが現代的なスタイルです。

使い方 3 ─ this() で別のコンストラクタを呼ぶ

コンストラクタが複数ある (オーバーロードしている) とき、共通の初期化処理を別のコンストラクタに任せたいことがあります。そのときに使うのが this(...) という構文です。

Java

public class Book { private String title; private int price; public Book() { this("無題", 0); // もう一つの Book(String, int) を呼ぶ } public Book(String title, int price) { this.title = title; this.price = price; } }

Book() (引数なし) は中で this("無題", 0) を呼んでいます。つまり「引数なしで作られた Book は、暗黙的に Book("無題", 0) と同じ」というショートカットです。初期化ロジックを 1 箇所に集約できるので、メンテナンス性が一気に上がります。

this(...)コンストラクタの先頭行 にしか書けません。途中や末尾に書くとコンパイルエラーになります。Java の仕様で「親クラスの初期化を 1 回だけ、しかも最初にやる」と決まっているからです。

動きを追ってみる

this がどう流れるか、簡単なシーケンス図で確認しましょう。

diagram (will load when visible)

メソッドチェーンの間も this はずっと book1 を指し続けます。だからこそ複数の setter を 1 行でつなげても、結果は同じ book1 1 つに反映されていきます。

よくある間違い

this まわりは、初心者と中級者でちょっとずつハマり方が変わります。代表的な落とし穴を 3 つ紹介します。

  • static メソッドの中で this を使ってしまうpublic static String describe() の中で this.title と書くと「non-static variable this cannot be referenced from a static context」と怒られます。static メソッドはクラス全体に属するので、「自分自身のインスタンス」が存在しません。インスタンスメソッド (static が付かないメソッド) でだけ this が使えます
  • this を省略したつもりが、ローカル変数を上書きしてしまうBook(String title) { title = title; } のように書くと、引数 title を引数自身に代入するだけでフィールドは空のまま。フィールドへ書き込みたいときは必ず this.title = title; と書くか、引数名を変える (_title newTitle など) ようにします
  • コンストラクタで this(...) を 2 行目以降に書くthis(...) はコンストラクタの 先頭行 でなければなりません。何か別の処理を挟むと「call to this must be first statement in constructor」というエラーになります。共通処理は this(...) で呼ばれる側のコンストラクタにまとめるのが鉄則です

似たキーワードに super がありますが、こちらは「親クラスのインスタンス」を指します。継承のレッスンで詳しく扱うので、ここではいったん「this の親バージョン」とだけ覚えておけば十分です。

やってみよう

今回の課題は、Solution.describeBook(String title, int price) を完成させて、"Java入門 (¥2000)" のような書式の文字列を返すメソッドを作ることです。

  1. 右側のエディタを開き、describeBook の中身を書く
  2. 戻り値は title + " (¥" + price + ")" の形にする
  3. テストを実行して全て緑になることを確認する

本文の Book クラス例では、フィールドと引数の同名衝突を this で解決していました。今回の Solution.describeBookstatic メソッドなので、メソッド内では this を使えませんが、本文の Book クラスの例を参考に、「もし Book インスタンスとして同じ機能を作るならどう書くか」を一緒にイメージしながら手を動かしてみてください。new Book("Java入門", 2000).describe() のように呼べる設計を頭の中で描けるようになれば、this の感覚が体に入った証拠です。

慣れてきたら、Book クラスを自分で作って、コンストラクタで this.title = title; this.price = price; を書き、describe() メソッドで同じ書式を返す ─ という練習も自由にやってみましょう。Java のオブジェクト指向の入り口は、この this の感覚を掴めるかどうかで景色が大きく変わります。

よくある質問

Q. Java と他言語の文法はどう違いますか?

A. Java は静的型付けで、変数宣言時に型を明示します(int x = 0;)。中括弧でブロックを表し、文末にセミコロンが必要です。Python の動的型付けや JavaScript の柔軟さに慣れていると最初は窮屈ですが、コンパイル時にバグが発見できる安全性がメリットです。

Q. コードが動かないときに最初に見るべき場所は?

A. コンパイルエラーは行番号とエラー種別(cannot find symbol、incompatible types など)が表示されます。実行時例外はスタックトレースの最初の at ... が原因行です。IDE(IntelliJ / VS Code)の警告も丁寧に潰すと、半分のバグは未然に防げます。

Q. Java の習得後に学ぶべき技術は何ですか?

A. 基本構文を抑えたら java-intermediate(コレクション / ジェネリクス / Stream / 例外)に進み、Spring Boot や Web 開発に展開するのが王道です。クラウド時代は Kotlin / Scala への展開も視野に入りますが、まずは Java 標準を固めるのが効率的です。

次のレッスン

次は カプセル化 (private と getter) で、カプセル化 (private と getter) を学びます。

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

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

理解度チェック (30 秒)

Q. this とは何か、1 文で説明してください。

この章のポイント

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

関連レッスン

要件

  1. クラス名は Solution、メソッド名は describeBook のままにすること
  2. 戻り値は title + " (¥" + price + ")" の書式の String
  3. 半角スペース、半角カッコ ( )、円記号 ¥ の位置と種類を正確に揃えること

入出力例

test-cases.txt

describeBook("Java入門", 2000)"Java入門 (¥2000)" describeBook("SQL基礎", 1500)"SQL基礎 (¥1500)" describeBook("無題", 0)"無題 (¥0)" describeBook("Effective Java", 4400)"Effective Java (¥4400)"

ヒント

main.java
main.java
学習モード

メモ

this キーワード

⌘S で保存