JavaのEnumクラスcloneメソッドを完全解説!初心者でもわかるenumが複製できない理由
生徒
「Javaのenumで作った列挙定数を、cloneメソッドを使って複製することはできますか?」
先生
「Javaのjava.lang.Enumにはcloneメソッドがありますが、実は列挙定数を複製するためには使えません。呼び出すと複製が拒否される特別な仕組みになっています。」
生徒
「メソッドが用意されているのに、コピーできないんですか?」
先生
「はい。enumでは同じ列挙定数のインスタンスが勝手に増えないことが重要です。cloneが使えない理由と、enumをコピーしたいように見える場面でどう考えればよいかを順番に確認しましょう。」
1. java.langのEnumクラスとは?
JavaのEnumクラスは、列挙型を表すenumの共通の基底クラスです。Javaでenumを使って列挙型を宣言すると、その列挙型は内部的にjava.lang.Enumを継承する形になります。開発者が通常のクラスのように明示的にextendsを書く必要はありません。Javaコンパイラが列挙型として必要な仕組みを用意してくれます。
enumは、取り得る値をあらかじめ限定したいときに便利です。例えば、注文状態を受付中、発送済み、取消済みのように決めたい場合や、曜日、会員ランク、処理結果、画面モードなどを表したい場合に利用できます。単なる文字列で状態を管理すると入力間違いが起こる可能性がありますが、enumを使えば利用できる値をプログラム上で明確に限定できます。
Enumクラスには、列挙定数の名前を取得するname、宣言された順番を取得するordinal、列挙定数を比較するcompareToなどのメソッドがあります。そして、Javaのオブジェクトでよく知られているcloneも存在します。ただし、Enumのcloneは一般的なクラスのcloneとは目的が大きく異なります。
2. Enumのcloneメソッドとは?
java.lang.Enumのcloneメソッドは、列挙定数を複製させないために用意されているメソッドです。一般的なcloneという名前からは、オブジェクトのコピーを作る機能を想像しやすいですが、Enumの場合は逆です。列挙定数がcloneによって増殖しないようにするため、呼び出された場合はCloneNotSupportedExceptionが発生します。
メソッドはprotected final Object clone() throws CloneNotSupportedExceptionという形で定義されています。protectedなので通常の外部コードから自由に呼び出せる公開メソッドではありません。またfinalが付いているため、独自のenumでcloneを上書きして複製できるように変更することもできません。
Enumのcloneは列挙定数をコピーする便利メソッドではなく、列挙定数が複製されないことを保証するための仕組みだと覚えると理解しやすくなります。
Java初心者がAPI一覧を見たときは、cloneという名前だけを見て利用できると思ってしまうことがあります。しかし、アクセス修飾子、finalの有無、例外の説明まで確認すると、このメソッドが通常の複製用途ではないことが分かります。
3. enumのcloneを外部から直接呼び出せない理由
まずは、普通のJavaコードから列挙定数のcloneを呼び出そうとした場合を考えてみましょう。次の例では、状態を表すStatusというenumを作り、Status.ACTIVEをcloneしようとしています。
enum Status {
ACTIVE,
INACTIVE
}
public class EnumCloneExample {
public static void main(String[] args) {
Status status = Status.ACTIVE;
Object copied = status.clone();
}
}
このコードはそのままではコンパイルできません。Enumのcloneはprotectedとして定義されているため、通常の利用側のクラスからstatus.clone()のように直接呼び出せないからです。publicメソッドではないため、列挙定数を受け取った側が自由にコピーする使い方は想定されていません。
ここで重要なのは、アクセスできるように工夫すれば普通に複製できるという意味ではないことです。Enumのcloneは実際に処理されてもCloneNotSupportedExceptionを投げるように作られています。つまり、外部から呼びにくくしているだけでなく、列挙定数そのものを複製させない設計になっています。
4. enumが複製できないのは列挙定数の一意性を守るため
enumがcloneできない理由を理解するには、列挙定数の特徴を押さえることが大切です。例えばStatus.ACTIVEという列挙定数がある場合、プログラムではその定数を決められた一つのインスタンスとして扱います。同じ名前の列挙定数がcloneによって何個も作れるようになると、enumが持つ分かりやすい性質が崩れてしまいます。
enumでは、同じ列挙定数かどうかを==で比較できます。これは列挙定数が決められたインスタンスとして管理されるためです。文字列の内容を比較するような感覚ではなく、同じ列挙定数そのものを参照しているかを安全に判定できます。
enum Status {
ACTIVE,
INACTIVE
}
public class EnumCompareExample {
public static void main(String[] args) {
Status first = Status.ACTIVE;
Status second = Status.ACTIVE;
System.out.println(first == second);
}
}
true
この例では、二つの変数に同じStatus.ACTIVEを代入しています。変数は二つありますが、別々のACTIVEオブジェクトが新しく作られているわけではありません。どちらも同じ列挙定数を参照しているため、比較結果はtrueになります。
もしcloneによってACTIVEのコピーを新しく作成できると、見た目はACTIVEなのに元のACTIVEとは異なるインスタンスが存在することになります。Javaではそのような状態を防ぐためにEnumのcloneをfinalとして実装し、列挙定数の複製を禁止しています。
5. enum内部からcloneを呼び出しても複製できない
Enumのcloneはprotectedなので、Enumを継承している列挙型の内部からはアクセスできる場面があります。しかし、アクセスできることと複製できることは別です。列挙型の内部からcloneを呼び出してみても、実際にはCloneNotSupportedExceptionが発生します。
enum Color {
RED,
BLUE;
public Object tryClone() {
try {
return super.clone();
} catch (CloneNotSupportedException e) {
System.out.println("enumは複製できません");
return null;
}
}
}
public class EnumCloneExceptionExample {
public static void main(String[] args) {
Object copied = Color.RED.tryClone();
System.out.println(copied);
}
}
enumは複製できません
null
この例から分かるように、Enumのcloneはコピーしたオブジェクトを返すための処理ではありません。cloneが呼び出されると例外によって複製が拒否されます。APIの戻り値はObject型ですが、正常にコピーが返されることを期待して使うメソッドではない点に注意しましょう。
また、cloneにはfinalが指定されているので、列挙型側でcloneを上書きし、独自にコピーを返すような実装へ変更することもできません。Javaの言語仕様とEnumクラスの設計によって、列挙定数の一意性が守られています。
6. enumを別の変数で使いたいときはそのまま代入する
enumを別の変数でも使いたいだけなら、cloneは必要ありません。通常は列挙定数をそのまま別の変数へ代入します。この場合、新しい列挙定数を作るわけではなく、同じ列挙定数への参照を別の変数に保存します。
enum Level {
LOW,
MIDDLE,
HIGH
}
public class EnumAssignmentExample {
public static void main(String[] args) {
Level original = Level.HIGH;
Level copiedReference = original;
System.out.println(original);
System.out.println(copiedReference);
System.out.println(original == copiedReference);
}
}
HIGH
HIGH
true
一般的な変更可能オブジェクトでは、同じインスタンスを二つの変数から参照すると、一方から内容を変更したときの影響を考える必要があります。一方、enumの列挙定数そのものを別の列挙定数へ書き換えることはできません。変数に別の定数を再代入することはできますが、それによって元の列挙定数が変更されるわけではありません。
例えばoriginalにHIGHが入っている状態で、copiedReferenceへLOWを再代入しても、HIGHという列挙定数そのものがLOWへ変化することはありません。この特徴があるため、状態や種類を表す値としてenumは扱いやすく、cloneによる複製を必要とする場面も基本的にはありません。
7. valueOfで取得しても同じ列挙定数が返される
文字列からenumの列挙定数を取得したい場合には、valueOfを使う方法があります。例えば設定ファイルや画面から受け取った文字列が列挙定数名と一致する場合、その名前に対応する列挙定数を取得できます。この場合も新しいenumオブジェクトが作られるわけではなく、宣言済みの列挙定数が取得されます。
enum Mode {
NORMAL,
SIMPLE
}
public class EnumValueOfExample {
public static void main(String[] args) {
Mode first = Mode.NORMAL;
Mode second = Mode.valueOf("NORMAL");
System.out.println(first == second);
}
}
true
このように、直接Mode.NORMALを参照した場合と、valueOfでNORMALを取得した場合でも同じ列挙定数として扱われます。enumは必要な定数を新しく複製して使うのではなく、あらかじめ定義された列挙定数を参照して利用するものだと考えると分かりやすくなります。
Java初心者がenumを学ぶときは、通常のクラスのようにnewで好きなだけインスタンスを作るものではないという点を意識しましょう。列挙定数はenum宣言の中で決めておき、プログラムではその中から必要な定数を選んで使います。
8. valuesで取得した配列のcloneとは区別して考える
enumを扱っていると、valuesメソッドで列挙定数の配列を取得することがあります。この配列に対してcloneを使うことは可能です。しかし、これはEnumクラスのcloneを使って列挙定数を複製しているわけではありません。配列オブジェクトを複製しているだけなので、意味がまったく異なります。
enum Size {
SMALL,
MEDIUM,
LARGE
}
public class EnumArrayCloneExample {
public static void main(String[] args) {
Size[] first = Size.values();
Size[] second = first.clone();
System.out.println(first == second);
System.out.println(first[0] == second[0]);
}
}
false
true
最初の比較がfalseになるのは、firstとsecondが別々の配列オブジェクトだからです。一方、配列の中に入っているSize.SMALLなどの列挙定数は複製されていません。そのため、先頭要素を比較するとtrueになります。
この違いはcloneを学ぶうえで重要です。配列のcloneが成功したからといって、enumの列挙定数までコピーされたわけではありません。配列という入れ物は別にできますが、その中で参照している列挙定数は同じものです。Javaのenum cloneについて調べるときは、Enumクラスのcloneと配列のcloneを混同しないようにしましょう。
9. Enumのcloneで初心者が覚えておきたいポイント
Javaのjava.lang.Enumにcloneメソッドがあることを知ったとき、一般的なオブジェクトと同じようにenumも複製できると考えやすいですが、実際には逆の目的を持っています。Enumのcloneはprotectedであり、さらにfinalとして定義されています。そして実際に呼び出された場合はCloneNotSupportedExceptionを発生させ、列挙定数が複製されることを防ぎます。
この仕組みがあることで、同じ列挙定数が複数の別インスタンスとして作られることを防げます。そのため、enumでは同じ定数を==で分かりやすく比較できます。注文状態、権限、曜日、処理区分、画面種別など、決められた選択肢を安全に扱いたい場面でenumが使いやすい理由の一つです。
enumの値を別の場所で使いたい場合は、cloneでコピーしようとせず、列挙定数をそのまま変数へ代入します。文字列から取得したい場合はvalueOf、一覧を取得したい場合はvaluesなど、目的に合った仕組みを利用します。valuesが返した配列をcloneすることはできますが、それは配列のコピーであり、列挙定数そのもののコピーではありません。
また、Cloneableを実装すればenumも複製できるのではないかと考える必要もありません。Enumのcloneはfinalなので、独自の列挙型で上書きして複製処理へ変更することはできません。そもそもenumは自由にインスタンスを増やすためのクラスではなく、宣言された列挙定数を共有して利用する仕組みです。
Enumのcloneを見かけたら、コピー方法を覚えるのではなく、Javaではenum定数をcloneできないことを保証するメソッドだと理解するのが重要です。
JavaのEnumクラスを学ぶときは、cloneだけを単独で覚えるより、列挙定数がどのように管理されるのか、なぜnewやcloneで自由に増やせないのか、==で比較できるのはなぜかまで一緒に理解すると、enumの設計思想が分かりやすくなります。これらを押さえておけば、Java初心者でも列挙型を安全に使い分けられるようになります。
まとめ
Javaのjava.lang.Enumクラスに用意されているcloneメソッドは、一般的なJavaオブジェクトのように列挙定数を複製するための機能ではありません。むしろ、enumで宣言された列挙定数がcloneによって複製されないようにするための仕組みです。Java初心者がEnumクラスのメソッド一覧を見たときは、cloneという名前からコピーを作れるように感じることがありますが、Enumの場合は意味が大きく異なります。
Javaのenumは、曜日、注文状態、会員ランク、処理結果、権限、画面モードなど、あらかじめ決められた選択肢を安全に表現したいときに便利な仕組みです。それぞれの列挙定数は決められた一つのインスタンスとして扱われるため、同じ名前の列挙定数が自由に複製されて増えてしまうと、enumが持つ一意性という大切な特徴が崩れてしまいます。Enumのcloneは、そのような複製を防ぐ役割を持っています。
Enumのcloneは列挙定数をコピーするメソッドではない
通常のJavaクラスでcloneという名前を見ると、現在のオブジェクトと同じ内容を持つコピーを作る処理を想像しやすいでしょう。しかし、java.lang.Enumのcloneでは、複製処理を正常に完了させることはできません。Enumのcloneが実行されると、CloneNotSupportedExceptionが発生するようになっています。
さらに、Enumのcloneはprotectedとして定義されています。そのため、通常のクラスから列挙定数に対して自由にcloneを呼び出すこともできません。またfinalが付いているので、独自に作成したenum側でcloneをオーバーライドし、列挙定数を複製できる処理へ変更することもできません。
JavaのEnumクラスでcloneを見かけたときは、列挙定数をコピーする方法ではなく、enumの複製を禁止するための仕組みだと覚えておくと分かりやすくなります。
外部のクラスからenumのcloneは直接呼び出せない
Enumのcloneはpublicではなくprotectedなので、通常の利用側から列挙定数に対して直接呼び出すことはできません。例えば、状態を表すStatusというenumを作り、その列挙定数にcloneを呼び出そうとしても、そのままではコンパイルエラーになります。
enum Status {
ACTIVE,
INACTIVE
}
public class EnumCloneExample {
public static void main(String[] args) {
Status status = Status.ACTIVE;
Object copied = status.clone();
}
}
このコードが使えない理由は、列挙定数を自由に複製する使い方がJavaで認められていないからです。アクセスできないことだけが理由ではなく、Enumのcloneそのものが列挙定数をコピーしないように設計されています。そのため、Javaでenumの値を別の変数へ渡したいだけなら、cloneを利用する必要はありません。
enumを別の変数で使う場合はそのまま代入する
Javaのenumを別の変数でも利用したい場合は、列挙定数をそのまま代入します。例えば、会員ランクを表すLevelというenumがある場合、Level.HIGHを別の変数へ代入しても、新しいHIGHが作成されるわけではありません。二つの変数が同じ列挙定数を参照します。
enum Level {
LOW,
MIDDLE,
HIGH
}
public class EnumAssignmentExample {
public static void main(String[] args) {
Level first = Level.HIGH;
Level second = first;
System.out.println(first);
System.out.println(second);
System.out.println(first == second);
}
}
HIGH
HIGH
true
このように、enumではコピーを作らなくても同じ列挙定数を複数の変数から安全に利用できます。通常の変更可能なオブジェクトでは、同じインスタンスを共有すると内容変更の影響を考える必要がありますが、enumの列挙定数そのものを別の列挙定数へ変更することはできません。変数へ別の列挙定数を再代入しても、元の列挙定数自体が変化するわけではありません。
enumでは同じ列挙定数を等価演算子で比較できる
enumの列挙定数が複製されないことは、比較処理にも関係しています。Javaでは、同じenum型の列挙定数であれば==を使って比較できます。例えば、二つの変数がどちらもStatus.ACTIVEを参照している場合、比較結果はtrueになります。
enum Status {
ACTIVE,
INACTIVE
}
public class EnumCompareExample {
public static void main(String[] args) {
Status first = Status.ACTIVE;
Status second = Status.ACTIVE;
if (first == second) {
System.out.println("同じ状態です");
}
}
}
同じ状態です
もしcloneによって見た目はACTIVEでも別のインスタンスを自由に作成できると、このような分かりやすい比較方法にも影響が出ます。Javaでは列挙定数の一意性を守ることで、状態や種類を安全に比較できるようにしています。Enumのcloneが複製を禁止している理由を理解すると、enumで等価演算子を利用しやすい理由も見えてきます。
valueOfを使っても新しいenumは作られない
Javaのenumでは、文字列から列挙定数を取得するときにvalueOfを利用できます。例えば、文字列のACTIVEからStatus.ACTIVEを取得できます。この場合も、新しいACTIVEオブジェクトが作成されるわけではありません。既に宣言されている列挙定数が取得されます。
enum Status {
ACTIVE,
INACTIVE
}
public class EnumValueOfExample {
public static void main(String[] args) {
Status first = Status.ACTIVE;
Status second = Status.valueOf("ACTIVE");
System.out.println(first == second);
}
}
true
直接Status.ACTIVEを指定した場合と、valueOfで文字列から取得した場合でも、同じ列挙定数を参照します。enumでは必要な値をコピーして増やすのではなく、enum宣言の中で用意された列挙定数を参照して利用するという考え方が基本です。
valuesの配列をcloneする処理とは意味が違う
enumには、すべての列挙定数を配列として取得できるvaluesがあります。この配列に対してcloneを使うことはできますが、Enumクラスのcloneとは別の話です。valuesで取得した配列をcloneした場合は、配列という入れ物が複製されるだけで、配列の中にある列挙定数そのものが新しく複製されるわけではありません。
enum Size {
SMALL,
MEDIUM,
LARGE
}
public class EnumArrayCloneExample {
public static void main(String[] args) {
Size[] first = Size.values();
Size[] second = first.clone();
System.out.println(first == second);
System.out.println(first[0] == second[0]);
}
}
false
true
一つ目の比較がfalseなのは、firstとsecondが別の配列だからです。一方、二つ目の比較がtrueなのは、どちらの配列にも同じSize.SMALLという列挙定数が入っているからです。Java初心者は、配列のcloneとEnumクラスのcloneを混同しないように注意しましょう。
Cloneableを使ってenumを複製することもできない
一般的なJavaクラスでcloneを学んだことがある人は、Cloneableを実装すればenumでも複製できるのではないかと考えるかもしれません。しかし、Enumのcloneはfinalで定義されているため、列挙型側で自由にオーバーライドできません。enumは通常のクラスとは異なり、列挙定数を決められた個数だけ管理するという特徴を持っています。
そのため、enumを通常のクラスと同じ感覚でコピーしようとする必要はありません。コピーしたいと思ったときは、本当に列挙定数自体を複製する必要があるのかを考えましょう。多くの場合は同じ列挙定数を別の変数から参照すれば十分です。
enumに追加した情報を扱う場合も列挙定数自体は複製しない
enumには列挙定数だけでなく、フィールドやコンストラクタ、メソッドを追加することもできます。例えば、会員ランクごとに表示名や数値を持たせることができます。この場合でも、列挙定数自体をcloneして増やす考え方にはなりません。定義済みの列挙定数から必要な情報を取得して利用します。
enum Rank {
BRONZE("一般会員"),
SILVER("優良会員"),
GOLD("特別会員");
private final String label;
Rank(String label) {
this.label = label;
}
public String getLabel() {
return label;
}
}
public class EnumFieldExample {
public static void main(String[] args) {
Rank rank = Rank.GOLD;
System.out.println(rank.getLabel());
}
}
特別会員
このように、enumは列挙定数ごとに関連情報を持たせることもできます。それでもGOLDをコピーして別のGOLDを作る必要はありません。決められた列挙定数を共有して利用するというenumの基本は変わりません。
Enumのcloneを理解するとenumの設計思想が分かる
Enumのcloneについて学ぶと、単に一つのメソッドの仕様だけでなく、Javaのenumがどのような考え方で作られているのかも理解できます。enumは自由にインスタンスを生成するための仕組みではなく、あらかじめ定義した限られた値を安全に利用するための仕組みです。
注文状態なら受付中、発送済み、取消済みだけを使う、会員ランクなら一般、優良、特別など決められた値だけを使うという設計に向いています。文字列だけで状態を管理するよりも入力間違いや想定外の値を減らしやすく、switch文や条件分岐でも扱いやすくなります。
cloneによる複製を禁止する仕組みも、この安全性を支える一つの要素です。JavaのEnumクラスを学ぶときは、cloneだけでなく、name、ordinal、valueOf、values、compareToなどの関連機能も一緒に学ぶと、列挙型の使い方をより深く理解できます。
Javaのenumを別の場所で使いたい場合はcloneではなく、その列挙定数をそのまま代入して利用します。Enumのcloneは列挙定数を複製させないための仕組みです。
生徒
「Enumのcloneは、列挙定数をコピーするためのメソッドではなかったんですね。」
先生
「はい。むしろ反対で、enumの列挙定数が複製されないようにするための仕組みです。実行されるとCloneNotSupportedExceptionが発生します。」
生徒
「別の変数でも同じenumを使いたい場合は、cloneせずにそのまま代入すればよいんですね。」
先生
「その通りです。同じ列挙定数を参照するだけなので、新しいインスタンスを作る必要はありません。」
生徒
「同じ列挙定数を等価演算子で比較できるのも、列挙定数が勝手に複製されないことと関係しているんですね。」
先生
「はい。列挙定数の一意性が守られているため、同じ定数かどうかを分かりやすく比較できます。状態管理でenumが便利な理由の一つです。」
生徒
「valuesで取得した配列をcloneできるのは、enumそのものをコピーしているわけではないんですよね。」
先生
「はい。コピーされているのは配列という入れ物だけです。中に入っている列挙定数は、元の配列と同じものを参照しています。」
生徒
「Enumのcloneは使い方を覚えるというより、なぜ使えないのかを理解することが大切なんですね。」
先生
「その理解で大丈夫です。Javaのenumは決められた値を安全に共有して使う仕組みです。cloneできない理由まで理解すると、Enumクラスや列挙型の特徴がより分かりやすくなります。」
ChatGPTなどの生成AIをこれから学びたい方に向けて、AIスキルアカデミーの口コミ・評判、料金、無料セミナーの内容を詳しくまとめています。
AIスキルアカデミーの口コミ・評判を詳しく見る