[Java] クラス複製(Clonable, Reflection)
こんにちは。明月です。
この投稿はJavaでクラス複製(Clonable, Reflection)に関する説明です。
クラス複製はプリミティブデータみたいにイコール記号(=)で簡単にデータを複製することができません。
クラスにはスタック値とヒープメモリがあります。クラスはヒープで割当て(インスタンス宣言)して、スタック値にポインタ参照を繋がれています。
ただ、イコール(=)でコピーするのはクラス複製じゃなくスタック値、つまり参照値をコピーすることです。
// テストのために作成したクラス
class Node {
// メンバ変数
private int data;
// プロパティ
public void setData(int data) {
this.data = data;
}
// 関数
public void print() {
System.out.println(data);
}
}
public class Example {
public static void main(String[] args) {
// Nodeクラスをa変数に割当した。
Node a = new Node();
// a変数にデータの5を格納した。
a.setData(5);
// b変数にコピーした。
Node b = a;
// データ10を格納した。
b.setData(10);
// a変数をコンソールに出力する。
a.print();
// b変数をコンソールに出力する。
b.print();
// a変数のハッシュコードを出力する。
System.out.println(a.hashCode());
// b変数のハッシュコードを出力する。
System.out.println(b.hashCode());
}
}
上の例をみればNodeクラスをa変数に割当しました。Node b変数にイコールでデータをコピーしました。
b変数に「10」のデータを格納しましたが、a変数のデータも「10」に代わりました。
ここでハッシュコードをみれば同じデータに出力します。
つまり「Node b = a」でクラスが新しく割当したことじゃなく、参照値(スタック値)だけコピーして結局、同じクラスを指すものになってしまいました。
我々が必要なことはクラスの複製です。
// クラスコピーの関数(clone)を使うためにインタフェースのCloneableを継承する。
class Node implements Cloneable {
// メンバ変数
private int data;
// プロパティ
public void setData(int data) {
this.data = data;
}
// 関数
public void print() {
System.out.println(data);
}
// clone関数を再定義する。
@Override
public Node clone() {
try {
// clonableインタフェースを継承すれば複製関数を使える。
return (Node) super.clone();
} catch (Throwable e) {
return null;
}
}
}
public class Example {
public static void main(String[] args) {
// Nodeクラスをa変数に割当した。
Node a = new Node();
// a変数にデータの5を格納した。
a.setData(5);
// b変数にコピーした。
Node b = a.clone();
// データ10を格納した。
b.setData(10);
// a変数をコンソールに出力する。
a.print();
// b変数をコンソールに出力する。
b.print();
// a変数のハッシュコードを出力する。
System.out.println(a.hashCode());
// b変数のハッシュコードを出力する。
System.out.println(b.hashCode());
}
}
上の例をみれば、確かに「a」の変数と「b」の変数に各クラスが割当しています。
b変数にデータを格納してもa変数に影響がありません。ハッシュコードも別の値を出力するので確かに別のクラスです。
上の方法がJavaで一般的なクラスコピーです。
でもすべてのクラスが「Cloneable」を継承しているなら問題ないですが、実際には継承してないクラスもたくさんあります。
そのクラスはどのようにクラスを複製しましょうか。
// テストのために作成したクラス
class Node {
// メンバ変数
private int data;
// プロパティ
public void setData(int data) {
this.data = data;
}
// 関数
public void print() {
System.out.println(data);
}
}
public class Example {
// クラスを複製する関数。
@SuppressWarnings("unchecked")
public static
T clone(T obj) {
try {
// クラスタイプを取得する。
Class
clz = (Class
)obj.getClass();
// コンストラクタを取得する。
Constructor
cons = clz.getDeclaredConstructor();
// クラスを新しく割当した。
T clone = cons.newInstance();
// メンバ変数をすべてコピーする。(privateタイプ含む)
for (Field field : clz.getDeclaredFields()) {
field.setAccessible(true);
Object data = field.get(obj);
field.set(clone, data);
}
// 新しく割当てしたクラスを返却する。
return clone;
} catch (Throwable e) {
return null;
}
}
public static void main(String[] args) {
// Nodeクラスをa変数に割当した。
Node a = new Node();
// a変数にデータの5を格納した。
a.setData(5);
// b変数にコピーした。
Node b = clone(a);
// データ10を格納した。
b.setData(10);
// a変数をコンソールに出力する。
a.print();
// b変数をコンソールに出力する。
b.print();
// a変数のハッシュコードを出力する。
System.out.println(a.hashCode());
// b変数のハッシュコードを出力する。
System.out.println(b.hashCode());
}
}
今回はクラス複製関数を作成してコピーしました。クラスの場合は一般クラスで「Cloneable」を継承してないです。
clone関数でクラスを新しくクラスを割当てしてメンバ変数をすべてコピーします。そのためクラス複製効果になります。
私の場合はクラスの修正ができない状況かつ継承もできない状況では上の方法でクラス複製をよくします。
ここまでJavaでクラス複製(Clonable, Reflection)に関する説明でした。
ご不明なところや間違いところがあればコメントしてください。
- [Java] POIを利用してExcelを扱う方法2020/03/17 01:48:00
- [Java] PDFを出力する方法(itextpdf)2020/03/13 00:47:31
- [Java] ログライブラリ(log4j)を使う方法2020/03/12 00:54:39
- [Java] Jsonタイプのデータを使う方法(Gsonライブラリ)2020/03/11 00:30:15
- [Java] Base64をエンコード、デコードする方法2020/03/09 10:24:01
- [Java] cmdコマンドを実行するための方法2020/03/06 18:01:10
- [Java] メール(javax.mail)を発送する方法2020/03/05 20:07:49
- [Java] クラス複製(Clonable, Reflection)2020/03/05 00:03:19
- [Java] シリアライズ(直列化: Serializable)2020/03/03 00:03:33
- [Java] StringBuilderとStringBufferの差異2020/03/02 07:52:22
- [Java] Compare関数を使う方法2020/02/29 03:00:00
- [Java] 数字フォーマット(お金表示及び小数点以下表示)2020/02/28 03:00:00
- [Java] サーブレット環境で現在の実行ディレクトリを取得する方法2020/02/27 03:00:00
- [Java] 日本語をユニコードに変換して、ユニコードから日本語に変換する方法2020/02/26 03:00:00
- [Java] コンソールからデータを受け取る方法(System.in)2020/02/25 03:00:00
- check2024/04/10 19:03:53
- [Java] 64.Spring bootとReactを連結する方法(Buildする方法)2022/03/25 21:02:18
- [Javascript] Node.jsをインストールしてReactを使う方法2022/03/23 18:01:34
- [Java] 63. Spring bootでcronスケジューラとComponentアノテーション2022/03/16 18:57:30
- [Java] 62. Spring bootでWeb-Filterを設定する方法(Spring Security)2022/03/15 22:16:37
- [Java] JWT(Json Web Token)を発行、確認する方法2022/03/14 19:12:58
- [Java] 61. Spring bootでRedisデータベースを利用してセッションクラスタリング設定する方法2022/03/01 18:20:52
- [Java] 60. Spring bootでApacheの連結とロードバランシングを設定する方法2022/02/28 18:45:48
- [Java] 59. Spring bootのJPAでEntityManagerを使い方2022/02/25 18:27:48
- [Java] 58. EclipseでSpring bootのJPAを設定する方法2022/02/23 18:11:10
- [Java] 57. EclipseでSpring bootを設定する方法2022/02/22 19:04:49
- [Python] Redisデータベースに接続して使い方2022/02/21 18:23:49
- [Java] Redisデータベースを接続して使い方(Jedisライブラリ)2022/02/16 18:13:17
- [C#] Redisのデータベースを接続して使い方2022/02/15 18:46:09
- [CentOS] Redisデータベースをインストールする方法とコマンドを使い方2022/02/14 18:33:07