質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.35%
Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

Q&A

解決済

2回答

2044閲覧

equalsのオーバーライドにおけるinstanceofとダウンキャストの意味

Isikoro

総合スコア9

Java

Javaは、1995年にサン・マイクロシステムズが開発したプログラミング言語です。表記法はC言語に似ていますが、既存のプログラミング言語の短所を踏まえていちから設計されており、最初からオブジェクト指向性を備えてデザインされています。セキュリティ面が強力であることや、ネットワーク環境での利用に向いていることが特徴です。Javaで作られたソフトウェアは基本的にいかなるプラットフォームでも作動します。

0グッド

1クリップ

投稿2020/02/16 02:34

現在学習しているjavaの参考書で不明点があります。
プログラミング初心者で要領を得ない部分があると思いますが、どうぞ宜しくお願いいたします。
※ 文章の太字が疑問部分です。

java

1public class Hero { 2 String name; 3 int hp; 4 public boolean equals(Object o) { 5 if(this == o) { return true; } 6 if(o instanceof Hero){       //(1) 7 Hero h = (Hero)o;         //(2) 8 if(this.name.equals(h.name)) {  //(3) 9 return true; 10 } 11 } 12 return false; 13 } 14}

java

1public class Main{ 2 public static void main(String[] args){ 3 Hero h1 = new Hero(); 4 h1.name = "ミナト"; 5 h1.hp = 100; 6 Hero h2 = new Hero(); 7 h2.name = "ミナト"; 8 h2.hp = 100; 9 if(h1.equals(h2) == true){ 10 System.out.println("同じ内容です"); 11 } else { 12 System.out.println("違う内容です"); 13 } 14 } 15}

疑問1

※ //のコメントは自分なりの理解を書いたものです

<Heroクラス>
(1) if(o instanceof Hero){     //oをHero型に代入可能かどうかをチェック
(2) Hero h = (Hero)o;      //(1)が代入可能ならoをHero型と見なしてhに代入
(3) if(this.name.equals(h.name))  //this.name(つまりh1.name)がh.name(つまりh2.name)と等しいかどうかをチェック

(a) (1)でチェックする理由がよく分かりません。

(b) (1)で代入可能かチェック済みなのに、わざわざ(2)でoをhに実際に代入(ダウンキャスト)する意味が分かりません。
つまり次のように(2)は省略しても両者の比較は可能なのでは?という疑問です。

ちなみに(2)を省略する形で変更したコードが次です。

(1) if(o instanceof Hero)     //oをHero型に代入可能かどうかを調べる
(2) //Hero h = (Hero)o;         //省略
(3) if(this.name.equals(o.name)) //this.name(つまりh1.name)がo.name(つまりh2.name)と正しいかどうかをチェック

疑問2(新たな疑問)

疑問1で変更したコード((2)を省略したコード)が下記です。
これをEclipseで動かしてみました。

java

1public class Hero { 2 String name; 3 int hp; 4 public boolean equals(Object o) { 5 if(this == o) { return true; } 6 if(o instanceof Hero){      //(1) 7 //Hero h = (Hero)o;         //(2)*省略* 8 if(this.name.equals(o.name)) {   //(3) 9 return true; 10 } 11 } 12 return false; 13 } 14}

すると、(3) if(this.name.equals(o.name))の行で、
「name は解決できないか、フィールドではありません」というエラーが出てしまいました。
このエラー原因を自分なりに調べた結果が以下です。

変更前の(3)の「h.name」で問題なく動いたのは、Heroクラスにてnameフィールドを定義していることでh.nameを使用できたため。
これに対し変更後の③では、oの継承元であるjava.lang.objectにおいてnameフィールドが定義されていないにもかかわらず、「h.name」が「o.name」と変更されてしまったため、o.nameを使用できないのでエラーになってしまった。
※ つまり子クラス型変数(ここではhのこと)を親クラス方変数(ここではoのこと)に代入した場合、子クラス型変数にしかないメソッドが親クラス方変数側から呼び出せないのと同じ理由。

つまり、「疑問1」の(b)の疑問の解答は上記(つまりnameフィールドを使用したいから)だと推測しましたが、この認識で合っているでしょうか?

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答2

0

(1)でチェックする理由がよく分かりません。

このメソッドのシグネチャがequals(Object o)と渡ってくるように、oには任意のオブジェクトが渡ってくる可能性があります。そして、equalsの実装ルールとして、

  • oにどのような引数を渡しても、例外になってはならない)
  • onullが渡された場合はfalseを返す
  • abnullでない場合は、a.equals(b)b.equals(a)の結果は一致しなければならない

というものがあります(Oracle)。これを守るために、「他の型のオブジェクト(もしくはnull)が来た場合はfalseを返す」実装が行われています。

「疑問1」の(b)の疑問の解答は上記(つまりnameフィールドを使用したいから)だと推測しましたが、この認識で合っているでしょうか?

はい、オブジェクト自体の型と、変数としての型は別個に存在しますので、型を合わせないとHeroオブジェクトとして使うことはできません。

投稿2020/02/16 02:46

編集2020/02/16 02:50
maisumakun

総合スコア146018

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Isikoro

2020/02/16 04:41 編集

ご教示いただきありがとうございました。 nullの場合も想定しないといけないのですね。 理解が深まりました。
guest

0

ベストアンサー

つまり、「疑問1」の(b)の疑問の解答は上記(つまりnameフィールドを使用したいから)だと推測しましたが、この認識で合っているでしょうか?

はい、そうです。Object oのままではHeroのフィールドを使った比較ができないため、Heroにキャストする必要があります。

(a) (1)でチェックする理由がよく分かりません。

引数がObjectなので、このequalsメソッドには**Hero以外のオブジェクトを渡すこともできます。**例えばStringやIntegerなど。そういったものを(1)のチェックなしに(2)のキャストを行うと、ClassCastExceptionで強制終了します。
Heroでないオブジェクトが渡った場合、明らかに「Heroとは異なるオブジェクト」であるため、異なるという結果、つまりfalseをこのメソッドには返してほしいのです。そのため、(1)のチェックを行い、違った場合は即座にfalseを返して終了するのです。

投稿2020/02/16 02:52

swordone

総合スコア20669

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

Isikoro

2020/02/16 04:38

ご教示いただきありがとうございました。 とても分かりやすく、おかげさまで疑問が晴れました。
Isikoro

2020/02/16 05:00

申し訳ありません。疑問が晴れたと書いたのですが新たに疑問が出てきました。 >Heroでないオブジェクトが渡った場合、明らかに「Heroとは異なるオブジェクト」であるため、異なるという結果、つまりfalseをこのメソッドには返してほしいのです。 これはつまり、equalsメソッドは同じオブジェクト同士(今回の場合はHero型同士)でしか比較できず、継承関係のない異なる自作のオブジェクト同士(例えばHero型とWizard型)では比較ができないということでしょうか?
swordone

2020/02/16 05:15

equalsの目的を再確認してください。 このオブジェクトと引数オブジェクトが「等しいかどうか」を判定するメソッドなのです。 結果として得たいのは「等しい=true」か「等しくない=false」なのです。 比較できないのではなく、型が違う時点で「等しくない」ことが確定できるので「それ以上比較する必要がない」のです。
Isikoro

2020/02/16 05:38 編集

今回の場合、nameの"ミナト"という文字列と、オブジェクトの型(Hero)の両方を比較しているということですね。(両方等ければtrue。どちらかが異なっていればfalse) ということは、もし異なるオブジェクト同士でname("ミナト")の文字列だけを比較したい場合(オブジェクトが異なっていても、文字列が等しい場合にtrueを返したい場合)は、そのような比較ができるように自分でオーバーライドのコードを変更すればよいのでしょうか?
swordone

2020/02/16 05:56

そうしたいならそうすればいいと思いますが、難しいと思いますよ。 異なるオブジェクトって、文脈から考えて「異なる型のオブジェクト」だと思うのですが、 型が異なる場合、文字列の変数名が何なのか、そもそも文字列を変数に持っているのかが不明で、 それを検証する手段も困難です。
maisumakun

2020/02/16 06:05

> もし異なるオブジェクト同士でname("ミナト")の文字列だけを比較したい場合 両方の型でそれを意識した実装にする必要があります(a.equals(b)とb.equals(a)は同じ結果でなければなりません)。相手の型が変更できない(equalsの実装が決まっている)場合、新しく実装する型ではfalseを返す一択です。
Isikoro

2020/02/16 06:21 編集

swordone様 ご指摘のとおり「異なる型のオブジェクト」のことです。 誤解を招く表現で申し訳ありません。 「異なる型のオブジェクト」同士を比較する場合はそういった問題が出てくるのですね。 maisumakun様 なるほど、両方の型で実装する必要があるのですね。 異なる型のオブジェクト同士で文字列を比較する方法があると分かっただけでも理解がより深まりました。 swordone様、maisumakun様、重ね重ねありがとうございました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.35%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問