[Java] クラス複製(Clonable, Reflection)


Devlopment note / Java    作成日付 : 2020/03/05 00:03:19   修正日付 : 2020/03/05 00:03:19

こんにちは。明月です。


この投稿は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)に関する説明でした。


ご不明なところや間違いところがあればコメントしてください。

最新投稿