0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

CloneNotSupportedExceptionについて

Last updated at Posted at 2023-11-06

はじめに

初投稿です。
今年の4月からWeb系のバックエンドエンジニアとして働き始めた1年目です。
アウトプットの機会を増やしたく、記事を投稿してみることにしました。
Markdown記法にも慣れていけたら良いなと思っています。誤りがあれば指摘していただけると助かります。

今回は、Java学習中に疑問に思った「CloneNotSupportedException」というエラーを扱おうと思います。

背景

私はclone()について、下記のような認識でいました。

  • 自作のクラスにclone()を実装する際には、必ずCloneableインターフェースをimplementsする必要がある。また、Cloneableインターフェースはマーカーインターフェースであり、実際には何も宣言されていない。
  • Objectクラスにはclone()メソッドがあり、全てのクラスは暗黙的にObjectクラスを継承している

疑問

上記の認識で、下記の2点を疑問に思いました。

  • そもそもCloneableインターフェースはマーカーインターフェースであるため、clone()メソッドの実装を強制しているわけではないので、エラーとは関係ないはず
  • 仮にCloneableインターフェース内でclone()が宣言されていたとしても、Objectクラスを継承しているのだからエラーを吐くのはおかしい(そもそも実装していないことによるエラーならコンパイルエラーになるはず)

要するに何がエラーを引き起こしてるのかを疑問に思いました。

ドキュメントを見る

そこで、Object#clone()のドキュメントを読んだところ、どうやらObject#clone()メソッドで、implements Cloneableを記述していない場合にエラーが出るよう実装されているようです。

つまり「背景」に記述した私の1つ目の認識は正確には間違っていて、自作クラスのclone()内部で、スーパークラス(正確にはObjectクラスの)clone()を利用していると、implements Cloneableの記載が必須になるようです。言語仕様によるエラーではなく、clone()がそのような実装になっているようです。
※ちなみにObject#clone()の実装をソースファイルを見て確認しようと思いましたが、native修飾子がついており、他の言語で実装されているようです。

実際に検証してみた

Mainクラスは下記です。

Main.java
public class Main {
    public static void main(String[] args) {
        Hero hero = new Hero("太郎");
        Hero hero3 = null;
        
        try {
            hero2 = hero.clone();
        } catch (CloneNotSupportedException e) {
            System.out.println(e.getMessage());
            System.out.println("クローンの作成に失敗");
        }
        
        System.out.println(hero3);
    }
}

Object#clone()を利用したHeroクラス

次に、Heroクラスです。
このHeroクラスのclone()内部では、親クラス、つまりObjectクラスのsuper()を利用しています。

Hero.java
public class Hero implements Cloneable {
    private String name;
    public Hero(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    @Override
    public Hero clone() throws CloneNotSupportedException {
        Hero hero = (Hero)super.clone();
        System.out.println("Heroのクローンを作成");
        return hero;
    }
}

Heroクラスを利用した場合、implements Cloneableを削除してもコンパイルは通りますが、実行時にCloneNotSupportedExceptionになります。

Object#clone()を利用しないHeroクラス

Hero.java
public class Hero {
    private String name;
    public Hero(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    @Override
    public Hero clone() {
        return new Hero(this.name);
    }
}

上記のHeroクラスは、clone()では新たなHeroインスタンスを作成し、それをリターンしているだけです。したがってimplements Cloneableは書かれていませんが無事にクローンが生成されます。MainクラスでCloneNotSupportedExceptionをハンドルする必要もありません。

まとめ

  • Object#cloneではimplements Cloneableの記述がなければCloneNotSupportedExceptionエラーになる
  • そのため、Object#clone()を利用していなければ、clone()を実装していたとしても実行時エラーにはならない。

終わりに

読んでくださった方、ありがとうございました。
今後も小さな発見でも時間があるときに投稿しようと思うので、見ていただけると嬉しいです。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?