Javaについて徹底解説!

Javaのthisを総ざらい! this/this()の意味・使い方

Javaでのthisは、使われる箇所によって以下の二つの意味があります。

  1. this:自分自身のインスタンスを指す変数
  2. this():コンストラクタ内から別のコンストラクタを呼び出す際のメソッド名

この記事ではそれぞれの説明を初級者向けに行いますので、ぜひしっかり理解していただければと思います。

なお、thisを理解するには、Javaにおける「インスタンス」の概念の理解が前提となります。もしインスタンスの理解に自信がない方は、先に参考書やインターネット上のドキュメントで、基礎的な理解をしておいてください。

1.変数のthis

1-1.thisは自分自身のインスタンスを指す変数

thisとは、自分自身のインスタンスを指す変数です。変数を使って、その変数が指すインスタンスのフィールドやメソッドを参照したり呼び出せるのと同じように、自分自身のインスタンスを明示的に指し示し、そのフィールドやメソッドを使いたい時に使う変数です。

thisの意味から当然なのですが、thisが指すモノはインスタンスごとに違います。例えば以下のプログラムでその違いの確認ができます(※)

※Object.toString()の標準実装はインスタンスの保存先アドレスを表現したものなので、同じインスタンスかどうかの区別に使えます。

インスタンスのコンテキスト(※)では、thisの有無にかかわらず現在のインスタンスのフィールド・メソッドを参照・実行できますので、明確な理由なくthisを付ける意味は薄いです。

※インスタンスメソッドやコンストラクタなど、インスタンスの持ち物としての文脈で実行されるコード。

ですから、thisが付けられるからと、全てのフィールド参照・メソッド呼び出しにthisを付けると、プログラムが冗長になります。Java向けの開発環境やエディタではフィールド・メソッドなどへは色分けをしてくれるのでなおさらです。ただし、コーディングルールとして付けることになっている場合があります。

1-2.thisの使い方

1-2-1.使い方の例:フィールドとローカル変数の名前を同じにしたい時

thisが最も使われるケースは、フィールドとローカル変数の名称を同じにしたい時です。Javaでの変数名はローカル変数が優先されるので、変数名が重複する場合、フィールドへはthisを付けて区別します。なお、これはsetterと呼ばれるパターンではほぼ必須です。

1-2-2.使い方の例:自分自身のインスタンスをメソッドの戻り値としたい場合

thisは自分自身のインスタンスをメソッドの戻り値としたい時にも使います。これはJavaプログラミングでのイディオムの一つでもあり、いろいろな所で見かけます。

thisを戻すことでメソッドチェーンが使えるようになり、直接的にはプログラムの行数を減らせます。これは標準APIStringBuilderを使う際のイメージが分かりやすいでしょう。

この考え方を推し進めると、以下のようなものとなります。各メソッドは単機能としながらも、何かのまとまった処理を連続して行うイメージです。このような記述をするAPIやフレームワークも多いのですが、興味がある方は「Fluent interface(流れるようなインターフェイス)」で検索すると更なる知識を得られるでしょう。

1-3.thisを使えるところ、使えないところ

1-3-1.thisはインスタンスのスコープで使える

thisは、Java 11の時点では以下のところで使えます。

  1. ①メソッドの中(インスタンスメソッド、デフォルトメソッド(Java 8以降)、ラムダ式(Java 8以降))
  2. ②コンストラクタの中
  3. ③インスタンスイニシャライザの中
  4. ④フィールド初期化時の右辺値の中
  5. ⑤Receiver Parameterとして(Java 8以降)

thisを使えるところの例としては以下のとおりです。無名クラスや内部クラスでのthisは少し注意が必要です(詳細は後述します)。

実行結果サンプル:

1-3-2.thisはクラスのスコープでは使えない

thisを使えるのはインスタンスのコンテキスト内だけです。つまり、クラスのコンテキスト、言い換えればstaticなコンテキスト内(staticメソッド・staticイニシャライザなど)ではthisを使うとコンパイルエラーになります。staticなコンテキスト内では、紐付いているインスタンスがないからです。

2.メソッドのthis()

2-1.this()は自分自身のコンストラクタを呼ぶ時に使う

Javaでプログラムを書いていると、あるコンストラクタから、オーバーロードした別のコンストラクタを明示的に呼びたい時があります。その時にthis()を使います。

コンストラクタの中から呼べるコンストラクタのメソッド名は“this”で固定なので、オーバーロードしたコンストラクタの内のどれを呼び出すかは、コンストラクタの引数で区別します。

以下の例で言うと、③→②→①の順番にコンストラクタが呼ばれ、①→②→③の順番で実行されていきます。

2-2.this()の使い方/コンストラクタの使い分け

this()の良くある使い道は、複数の引数を持つ共通的なコンストラクタを用意しておいて、呼び出すコンストラクタに応じて設定値を変えたりデフォルト値を設定する、というものです。

それによりインスタンスの初期化処理を一つのコンストラクタにまとめられるので、プログラムが分かりやすくなりますし、保守性も高まります。

この例だと、フィールドの実質的な初期化はコンストラクタで行っています。はデフォルト値を引数に、を呼び出しているだけです。これによりフィールドの初期化処理の記述をだけに出来ています。

3.thisに関する少し進んだ知識

3-1.【中級者向け】内部クラスのエンクロージングインスタンスを指すthis

内部クラスのインスタンス内から、そのインスタンスが紐づいているクラスのインスタンス(エンクロージングインスタンス)の参照を得たい場合は、少々特殊なthisの指定の仕方をします。

内部クラスの中から「クラス名.this」とすると、そのクラスのインスタンスを得られます。ややこしいのですが、イメージとしては以下のようにインスタンスが親子状に関係付けられていると考えてもらえればいいと思います。

  • ThisSample9のインスタンス
  •    ┗InnerClassのインスタンス

3-2.【中級者向け】synchronizedブロックでのthis

排他制御のため、以下の書き方をしているソースコードを多く見かけました。これは排他制御を行う際の一つのイディオムです。

これは、自分自身のインスタンスを排他制御のモニターとして使用しています。ですので、このsynchronizedブロックは、1インスタンスごとに必ず1つのスレッドからしか実行されないことが保証されます。

なお、今では排他制御にはjava.util.concurrentにある各種Lockクラスを使うことが多いので、synchronizedブロックを使うケースは減りました。ですが、昔の書籍やソースコードを読むこともあるでしょうから、意味はしっかり理解しておきたいものです。

3-3.【上級者向け】スーパークラスでのthis

継承関係のあるクラスでthisを使う時は注意が必要です。thisが実際に指しているものが何かを、メソッドのオーバーライドの観点から意識する必要があるからです。

以下の例は、Javaでのポリモーフィズムの特徴とthisをしっかり理解していないと、直感に反する動きをしていると思われるかもしれません。どういう値が表示されるか予想してみてください。

ThisSample10Childの実行結果例

  • Parent.methodOfSuperClass ←スーパークラスのメソッドが呼ばれた
  • this is ThisSample10Child@4411d970 ←thisはサブクラスのインスタンスを指す
  • this.instanceField is 123 ←this.instanceFieldはスーパークラスのフィールドを指す
  • Child.methodOverride ←サブクラスのオーバーライド済みメソッドが実行された
  • this is ThisSample10Child@4411d970 ←ここでのthisは、当然サブクラスのインスタンスを指す
  • this.instanceField is 456 ←this.instanceFieldはサブクラスのフィールドを指す

サブクラスから呼び出された場合、スーパークラスでのthisは、サブクラスのインスタンスを指します。ですので、メソッドをオーバーライドした場合に呼ばれるメソッドの実体は、サブクラスのものです(this経由でなくてもそうです)

そして、スーパークラスを直接newした場合のthisは、当然ながらスーパークラスのインスタンスを指します。

これこそがポリモーフィズムの作用です。thisが指すインスタンスの実体はコンテキストに応じて変わりますが、それをJava仮想マシンが把握しており、インスタンスに紐づけられたメソッドを適切に呼び分けているのです。

注意すべきはスーパークラス内のthis.instanceFieldが指すフィールドは、サブクラスから呼び出された場合でもスーパークラスのものだということです。Javaではフィールドはポリモーフィズムの対象とはならないからです。

4.まとめ

thisは自分自身のインスタンスを指す変数で、this()は自分自身のクラスのコンストラクタを呼び出す時に使います。

この記事ではそれぞれの良くある使い方、注意すべきことを紹介しましたが、いずれも効果的に使えば読みやすいプログラムを作れるものです。

そして、thisの使い方・考え方を突き詰めていくと、Javaでのオブジェクト指向の考え方がうっすらと見えてきます。関連するsuper/super()も含めてしっかりと理解して、一つ上のレベルのJavaプログラマーを目指しましょう!!