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

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

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

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

Q&A

1回答

1380閲覧

cloneメソッドの中身と呼び出し方

Isikoro

総合スコア9

Java

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

1グッド

0クリップ

投稿2020/06/05 07:51

手持ちの参考書で不明点があります。
初心者で独学でやっており間違っている部分が多々あるかもしれませんが、宜しくお願いいたします。

#(1)cloneメソッドの中身
手持ちの参考書に「すべてのクラスはobjectクラスからcloneメソッドを継承している」とあるので、cloneの継承元(objectクラスのcloneメソッド)をeclipseで調べてみました。
すると『protected native Object clone() throws CloneNotSupportedException;』となっていて、cloneメソッドの中身は空っぽでした。
しかしこのnative修飾子を調べてみると、この修飾子のついたメソッドはjava以外の言語でどこかの場所で書かれているとのことなので、もしそうであれば、cloneメソッドを呼び出すだけでオーバーライドしなくても複製(ディープコピーできるかは別にして)できるはずだと思い、次のコード(参考書のコードを一部変更)で検証してみました。

<コード①>

java

1public class Main { 2 3 public static void main(String[] args) { 4 Hero h1 = new Hero(); 5 Hero h2 = h1.clone(); /* ここで複製 */ 6 System.out.println("コピー元のhpは" + h1.hp + "/コピー先のhpは" +h2.hp); 7 } 8}

java

1public class Hero implements Cloneable { 2 int hp ; 3 4 public Hero clone() { 5 Hero result = new Hero(); 6 //result.hp = this.hp; //★この行を無効化するとhpが複製されない 7 return result; 8 }

実行結果

コピー元のhpは100/コピー先のhpは0

ところが、Heroクラスの★の行(手持ちの参考書では有効化されている)を無効化すると、実行結果のとおりhpは0となっていて複製できていないことが分かりました。(★の行を有効化するとコピー先もhpは100になります)

このことから、cloneメソッドはインタンスの複製(Hero result = new Hero();)からフィールドの複製(result.hp = this.hp;)まで全て行ってあげないと複製できない。
そうなると、objectクラスのcloneメソッドの中身は本当に空っぽ(機能していない)なのでは?という疑問が出てきました。

そこで、ネットから拾った次のコード(一部変更)で再度検証してみました。

<コード②>

java

1import java.util.Arrays; 2public class Main { 3 public static void main(String[] args) { 4 5 MyClass mc1 = new MyClass(); 6 MyClass mc2 = mc1.clone(); 7 8 mc1.iArr[0] = 5; 9 10 System.out.println( 11 "mc1:" + mc1.n + " " + Arrays.toString(mc1.iArr) 12 + "\n" + 13 "mc2:" + mc2.n + " " + Arrays.toString(mc2.iArr) 14 ); 15 } 16}

java

1public class MyClass implements Cloneable { 2 3 public String n ="あ"; 4 public int[] iArr = {1, 2, 3}; 5 6 //cloneのオーバーライド 7 @Override 8 public MyClass clone() { 9 MyClass res = null; 10 11 try { 12 // クローン 13 res = (MyClass)super.clone(); 14 } catch (CloneNotSupportedException e) { 15 throw new InternalError(e.toString()); 16 } 17 // ディープコピー 18 //res.iArr = iArr.clone(); //有効化するとディープコピーになる 19 return res; 20 }

実行結果

mc1:あ [5, 2, 3]
mc2:あ [5, 2, 3]

すると今回は、フィールドのnもiArrもきちんと複製できていることが分かりました。

最初の<コード①>ではオーバーライドしてその中身も全て実装しないとインスタンスもフィールドも複製できなかったのに、今回の<コード②>ではオーバーライドはしているものの、『res = (MyClass)super.clone();』とcloneメソッドを呼び出すだけで複製に成功しています。

質問まとめ

(1)objectクラスのcloneメソッドの中身は本当に存在(機能)しているのでしょうか?存在(機能)していないのでしょうか?存在するならその内容の確認方法を教えていただけますでしょうか?

(2)最初の<コード①>では複製できないのに、今回の<コード②>では複製できているのはなぜでしょうか?

cloneメソッドの呼び出し方

<コード①>と<コード②>でのcloneメソッドの呼び出し方の違いについての疑問ですが、以下の理解で合っていますでしょうか?
もし違っていましたらご指摘・訂正いただけると助かります。

<コード①>では、Mainクラスからcloneメソッドを呼んでいる。(Hero h2 = h1.clone();の部分)
つまりMainクラスから複製したいクラス(Heroクラス)を経由して継承元(Objectクラス)を呼び出すことになるのでsuperを利用せずに呼び出している。
(インタンス外部からメソッド呼び出しがあった場合に子インスタンスにそのメソッドが存在しないときは親インスタンスのメソッドを探しにいく性質を利用している)

 ※cloneを呼ぶ流れ:①Mainクラス ⇒ ②複製したいクラス(Heroクラス)⇒ ③継承元(Objectクラス)

<コード②>では、複製したいクラスにcloneメソッドを書いているため、複製したいクラス(MyClassクラス)つまり自分自身から継承元(Objectクラス)を呼び出すことになるのでsuperを利用している。

※cloneを呼ぶ流れ:①複製したいクラス(MyClassクラス)⇒ ②継承元(Objectクラス)

まとめ:<コード①>と<コード②>もcloneメソッドを呼び出そうとしていることは同じで呼び出し方が違うだけ。

gentaro👍を押しています

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2020/06/05 08:44

ちなみに cloneメソッドを呼び出すだけでオーバーライドしなくても複製(ディープコピーできるかは別にして)できるはずだと思い、次のコード(参考書のコードを一部変更)で検証してみました。 は アクセスレベルが protected なので外部ロジックでは複製できなかったりします。
guest

回答1

0

Object.clone()の動作の中身については、Javaのリファレンスに記載されていますが、シャローコピーまでは任せることができます。

cloneメソッドはインタンスの複製(Hero result = new Hero();)からフィールドの複製(result.hp = this.hp;)まで全て行ってあげないと複製できない。

super.clone()を呼んでいないので、Object.clone()実行されていません

(2)最初の<コード①>では複製できないのに、今回の<コード②>では複製できているのはなぜでしょうか?

super.clone()を呼んでいるかいないかの違いです。

<コード①>と<コード②>でのcloneメソッドの呼び出し方の違いについての疑問ですが、以下の理解で合っていますでしょうか?

理解は全く違います。どちらもMyClass.clone()を呼んでいるのであり、呼び方についての違いはありません。

投稿2020/06/05 08:02

maisumakun

総合スコア145201

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

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

maisumakun

2020/06/05 08:04

Javaの場合、オーバーライドしたメソッドからsuperのメソッドは、自動では呼び出されません。
maisumakun

2020/06/05 08:06

「<コード②>では、複製したいクラスにcloneメソッドを書いている」とありますが、それは<コード①>でも同じです(Hero.cloneメソッドを実装しています)。
maisumakun

2020/06/05 08:09

> <コード①>では、Mainクラスからcloneメソッドを呼んでいる。 書いてある場所の問題であり、呼んでいるメソッドはHero.cloneです。
Isikoro

2020/06/05 13:14 編集

ご回答ありがとうございます! >Object.clone()の動作の中身については、Javaのリファレンスに記載されていますが、シャローコピーまでは任せることができます。 >super.clone()を呼んでいないので、Object.clone()が実行されていません。 >super.clone()を呼んでいるかいないかの違いです。 ⇒ <コード②>のようにsuper.clone()で呼べばObject.clone()が実行されてシャローコピーまで実行できるというですね。 これに対し<コード①>はsuper.clone()で呼んでいないためObject.clone()が実行されないので、オーバーライドの中でインスタンスとフィールドをひとつひとつ実装しないと複製できないということですね。 このような理解で合っているでしょうか? ちなみにアドバイスをヒントに、<コード①>を<コード②>のようにsuper.clone()を呼ぶように次のように修正してみたところ、<コード②>と同様に複製できました! public class Hero implements Cloneable { int hp ; // HP //cloneのオーバーライド @Override public Hero clone() { Hero res = null; try { // クローン res = (Hero)super.clone(); } catch (CloneNotSupportedException e) { throw new InternalError(e.toString()); } return res; }
Isikoro

2020/06/05 13:20 編集

>Javaの場合、オーバーライドしたメソッドからsuperのメソッドは、自動では呼び出されません。 ⇒こちら全く知りませんでした。 ということは<コード①>については、私が最初に示した下記のcloneを呼ぶ流れは誤りで、 継承元(Objectクラス)のcloneをオーバーライドした、複製したいクラス(Heroクラス)のcloneをMainクラスから呼び出しているだけ、という理解で合っているでしょうか? (つまりObject.clone()を直接superで呼べていないので、Object.clone()は実行されていない) 【誤り】・・・①Mainクラス ⇒ ②複製したいクラス(Heroクラス)⇒ ③継承元(Objectクラス)
Isikoro

2020/06/05 13:20 編集

>「<コード②>では、複製したいクラスにcloneメソッドを書いている」とありますが、それは<コード①>でも同じです(Hero.cloneメソッドを実装しています)。 ⇒<コード②>では、複製したいクラスからcloneメソッドを呼んでいる(res = (MyClass)super.clone();)と訂正させてください。
Isikoro

2020/06/05 13:20 編集

>全く違います。どちらもMyClass.clone()を呼んでいるのであり、違いはありません。 ⇒この部分が理解できずにいます。 ※ 一連の返信が分かりづらくなってしまい申し訳ありません。。。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだベストアンサーが選ばれていません

会員登録して回答してみよう

アカウントをお持ちの方は

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

ただいまの回答率
85.47%

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

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

質問する

関連した質問