clone
日本語 | 複製、コピー、クローン |
英語 | clone |
ふりがな | くろーん |
フリガナ | クローン |
Objectクラスのclone()メソッド。
インスタンスを複製するためのメソッド。
このメソッドには複雑なルールがある。
まず、Objectクラスのclone()メソッドはprotectedメソッドのため、外のクラスから直接呼び出すことはできない。このメソッドはオーバーライドするか、またはオーバーライドしたclone()メソッド内で呼び出す。
サブクラスからObjectクラスのclone()メソッドを呼び出すことで、そのサブクラスの複製を作成することができる。サブクラスのインスタンスが作られ、フィールドの値が全てコピーされてから、Objectクラスのclone()メソッドの戻り値としてインスタンスの参照が返される。
ただし、Objectクラスのclone()メソッドは、サブクラスがCloneableインターフェイスの実装クラスでない場合、CloneNotSupportedException例外を投げる。つまりObjectクラスのclone()メソッドは、このメソッドが呼ばれることを証明するために、サブクラスにCloneableインターフェイスの実装を強制する。
Objectクラスのclone()メソッドによる複製は、フィールドの値をそのままコピーする。フィールドが参照型変数の場合、その参照値がそのままコピーされるため、参照先のインスタンスは複製されない。これを「浅いコピー」(シャローコピー)という。
インスタンスも複製する「深いコピー」(ディープコピー)を行う場合には、オーバーライドしたclone()メソッド内で各フィールドの参照型変数が参照するインスタンスの複製を作り、セットする必要がある。
clone()メソッドによる複製は面倒だが、Javaではクラスを参照を通してのみ管理することができ、参照の複製のみではインスタンスは複製されない以上、必須のシステムである。
Javaのクラスとインスタンス、参照を理解した上で、適切に実装できることが望まれる。
インスタンスを複製するためのメソッド。
このメソッドには複雑なルールがある。
まず、Objectクラスのclone()メソッドはprotectedメソッドのため、外のクラスから直接呼び出すことはできない。このメソッドはオーバーライドするか、またはオーバーライドしたclone()メソッド内で呼び出す。
サブクラスからObjectクラスのclone()メソッドを呼び出すことで、そのサブクラスの複製を作成することができる。サブクラスのインスタンスが作られ、フィールドの値が全てコピーされてから、Objectクラスのclone()メソッドの戻り値としてインスタンスの参照が返される。
ただし、Objectクラスのclone()メソッドは、サブクラスがCloneableインターフェイスの実装クラスでない場合、CloneNotSupportedException例外を投げる。つまりObjectクラスのclone()メソッドは、このメソッドが呼ばれることを証明するために、サブクラスにCloneableインターフェイスの実装を強制する。
Objectクラスのclone()メソッドによる複製は、フィールドの値をそのままコピーする。フィールドが参照型変数の場合、その参照値がそのままコピーされるため、参照先のインスタンスは複製されない。これを「浅いコピー」(シャローコピー)という。
インスタンスも複製する「深いコピー」(ディープコピー)を行う場合には、オーバーライドしたclone()メソッド内で各フィールドの参照型変数が参照するインスタンスの複製を作り、セットする必要がある。
clone()メソッドによる複製は面倒だが、Javaではクラスを参照を通してのみ管理することができ、参照の複製のみではインスタンスは複製されない以上、必須のシステムである。
Javaのクラスとインスタンス、参照を理解した上で、適切に実装できることが望まれる。
// Sample.java
public class Sample
{
public static void main( String[] args )
{
try
{
// DataNotUseSuperクラスの使用例。
// 元インスタンスを作ります。
DataNotUseSuper data = new DataNotUseSuper();
data.setData( 100 );
System.out.println( data.getData() );
// 100
// clone()メソッドで複製を作ります。
DataNotUseSuper dataCloned = (DataNotUseSuper)data.clone();
// 出力します。
System.out.println( dataCloned.getData() );
// 100
}
catch( CloneNotSupportedException e )
{
e.printStackTrace();
}
try
{
// DataUseSuperクラスの使用例。
// 元インスタンスを作ります。
DataUseSuper data = new DataUseSuper();
data.setData( 200 );
System.out.println( data.getData() );
// 200
// clone()メソッドで複製を作ります。
DataUseSuper dataCloned = (DataUseSuper)data.clone();
// 出力します。
System.out.println( dataCloned.getData() );
// 200
}
catch( CloneNotSupportedException e )
{
e.printStackTrace();
}
try
{
// DataNotCloneableImplementedクラスの使用例。
// 元インスタンスを作ります。
DataNotCloneableImplemented data = new DataNotCloneableImplemented();
data.setData( 300 );
System.out.println( data.getData() );
// 300
// clone()メソッドで複製を作ります。
DataNotCloneableImplemented dataCloned = (DataNotCloneableImplemented)data.clone();
}
catch( CloneNotSupportedException e )
{
// DataNotCloneableImplementedクラスは、Cloneableインターフェイス
// の実装クラスじゃないのに、Objectクラスのclone()メソッドで
// 複製を作ろうとしたので、CloneNotSupportedException例外が投げられます。
e.printStackTrace();
// java.lang.CloneNotSupportedException: DataNotCloneableImplemented
// at java.lang.Object.clone(Native Method)
// at DataNotCloneableImplemented.clone(Sample.java:170)
// at Sample.main(Sample.java:61)
}
// Objectクラスのclone()メソッドはprotectedなので、
// オーバーライドしないと呼び出せません。
DataNotCloneOverride data = new DataNotCloneOverride();
// data.clone();
// コンパイルエラー:
// メソッド clone() は型 Object で不可視です。
}
}
/**
* Objectクラスのclone()メソッドをオーバーライドして、
* 手作業でインスタンスを作りフィールドをコピーする例。
*/
class DataNotUseSuper
{
private int data;
public void setData( int i )
{
data = i;
}
public int getData()
{
return data;
}
/**
* Objectクラスのclone()メソッドをオーバーライドします。
*/
protected Object clone() throws CloneNotSupportedException
{
// 自クラスのインスタンスを作り、フィールドをコピーします。
// (自クラスなので、privateフィールドもアクセスできます)
DataNotUseSuper newData = new DataNotUseSuper();
newData.data = this.data;
return newData;
}
}
/**
* Objectクラスのclone()メソッドをオーバーライドして、
* Objectクラスのclone()メソッドを使用して複製を作ります。
*/
class DataUseSuper implements Cloneable
{
private int data;
public void setData( int i )
{
data = i;
}
public int getData()
{
return data;
}
/**
* Objectクラスのclone()メソッドをオーバーライドします。
*/
protected Object clone() throws CloneNotSupportedException
{
// Objectクラスのclone()メソッドで複製を作ります。
// Cloneableインターフェイスの実装クラスでないと
// CloneNotSupportedException例外が投げられます。
return super.clone();
}
}
/**
* Objectクラスのclone()メソッドをオーバーライドして、
* Objectクラスのclone()メソッドを使用して複製を作ります。
* ただし、Cloneableインターフェイスの実装クラスでは
* ありません(つまり間違った例です)。
*/
class DataNotCloneableImplemented
{
private int data;
public void setData( int i )
{
data = i;
}
public int getData()
{
return data;
}
/**
* Objectクラスのclone()メソッドをオーバーライドします。
*/
protected Object clone() throws CloneNotSupportedException
{
// Objectクラスのclone()メソッドで複製を作ります。
// でも、Cloneableインターフェイスの実装クラスではないので
// CloneNotSupportedException例外が投げられます。
return super.clone();
}
}
/**
* Objectクラスのclone()メソッドをオーバーライドしていいないクラス。
* これも間違った例です。
*/
class DataNotCloneOverride
{
private int data;
public void setData( int i )
{
data = i;
}
public int getData()
{
return data;
}
}
public class Sample
{
public static void main( String[] args )
{
try
{
// DataNotUseSuperクラスの使用例。
// 元インスタンスを作ります。
DataNotUseSuper data = new DataNotUseSuper();
data.setData( 100 );
System.out.println( data.getData() );
// 100
// clone()メソッドで複製を作ります。
DataNotUseSuper dataCloned = (DataNotUseSuper)data.clone();
// 出力します。
System.out.println( dataCloned.getData() );
// 100
}
catch( CloneNotSupportedException e )
{
e.printStackTrace();
}
try
{
// DataUseSuperクラスの使用例。
// 元インスタンスを作ります。
DataUseSuper data = new DataUseSuper();
data.setData( 200 );
System.out.println( data.getData() );
// 200
// clone()メソッドで複製を作ります。
DataUseSuper dataCloned = (DataUseSuper)data.clone();
// 出力します。
System.out.println( dataCloned.getData() );
// 200
}
catch( CloneNotSupportedException e )
{
e.printStackTrace();
}
try
{
// DataNotCloneableImplementedクラスの使用例。
// 元インスタンスを作ります。
DataNotCloneableImplemented data = new DataNotCloneableImplemented();
data.setData( 300 );
System.out.println( data.getData() );
// 300
// clone()メソッドで複製を作ります。
DataNotCloneableImplemented dataCloned = (DataNotCloneableImplemented)data.clone();
}
catch( CloneNotSupportedException e )
{
// DataNotCloneableImplementedクラスは、Cloneableインターフェイス
// の実装クラスじゃないのに、Objectクラスのclone()メソッドで
// 複製を作ろうとしたので、CloneNotSupportedException例外が投げられます。
e.printStackTrace();
// java.lang.CloneNotSupportedException: DataNotCloneableImplemented
// at java.lang.Object.clone(Native Method)
// at DataNotCloneableImplemented.clone(Sample.java:170)
// at Sample.main(Sample.java:61)
}
// Objectクラスのclone()メソッドはprotectedなので、
// オーバーライドしないと呼び出せません。
DataNotCloneOverride data = new DataNotCloneOverride();
// data.clone();
// コンパイルエラー:
// メソッド clone() は型 Object で不可視です。
}
}
/**
* Objectクラスのclone()メソッドをオーバーライドして、
* 手作業でインスタンスを作りフィールドをコピーする例。
*/
class DataNotUseSuper
{
private int data;
public void setData( int i )
{
data = i;
}
public int getData()
{
return data;
}
/**
* Objectクラスのclone()メソッドをオーバーライドします。
*/
protected Object clone() throws CloneNotSupportedException
{
// 自クラスのインスタンスを作り、フィールドをコピーします。
// (自クラスなので、privateフィールドもアクセスできます)
DataNotUseSuper newData = new DataNotUseSuper();
newData.data = this.data;
return newData;
}
}
/**
* Objectクラスのclone()メソッドをオーバーライドして、
* Objectクラスのclone()メソッドを使用して複製を作ります。
*/
class DataUseSuper implements Cloneable
{
private int data;
public void setData( int i )
{
data = i;
}
public int getData()
{
return data;
}
/**
* Objectクラスのclone()メソッドをオーバーライドします。
*/
protected Object clone() throws CloneNotSupportedException
{
// Objectクラスのclone()メソッドで複製を作ります。
// Cloneableインターフェイスの実装クラスでないと
// CloneNotSupportedException例外が投げられます。
return super.clone();
}
}
/**
* Objectクラスのclone()メソッドをオーバーライドして、
* Objectクラスのclone()メソッドを使用して複製を作ります。
* ただし、Cloneableインターフェイスの実装クラスでは
* ありません(つまり間違った例です)。
*/
class DataNotCloneableImplemented
{
private int data;
public void setData( int i )
{
data = i;
}
public int getData()
{
return data;
}
/**
* Objectクラスのclone()メソッドをオーバーライドします。
*/
protected Object clone() throws CloneNotSupportedException
{
// Objectクラスのclone()メソッドで複製を作ります。
// でも、Cloneableインターフェイスの実装クラスではないので
// CloneNotSupportedException例外が投げられます。
return super.clone();
}
}
/**
* Objectクラスのclone()メソッドをオーバーライドしていいないクラス。
* これも間違った例です。
*/
class DataNotCloneOverride
{
private int data;
public void setData( int i )
{
data = i;
}
public int getData()
{
return data;
}
}
// Sample.java public class Sample { public static void main( String[] args ) { try { // DataNotUseSuperクラスの使用例。 // 元インスタンスを作ります。 DataNotUseSuper data = new DataNotUseSuper(); data.setData( 100 ); System.out.println( data.getData() ); // 100 // clone()メソッドで複製を作ります。 DataNotUseSuper dataCloned = (DataNotUseSuper)data.clone(); // 出力します。 System.out.println( dataCloned.getData() ); // 100 } catch( CloneNotSupportedException e ) { e.printStackTrace(); } try { // DataUseSuperクラスの使用例。 // 元インスタンスを作ります。 DataUseSuper data = new DataUseSuper(); data.setData( 200 ); System.out.println( data.getData() ); // 200 // clone()メソッドで複製を作ります。 DataUseSuper dataCloned = (DataUseSuper)data.clone(); // 出力します。 System.out.println( dataCloned.getData() ); // 200 } catch( CloneNotSupportedException e ) { e.printStackTrace(); } try { // DataNotCloneableImplementedクラスの使用例。 // 元インスタンスを作ります。 DataNotCloneableImplemented data = new DataNotCloneableImplemented(); data.setData( 300 ); System.out.println( data.getData() ); // 300 // clone()メソッドで複製を作ります。 DataNotCloneableImplemented dataCloned = (DataNotCloneableImplemented)data.clone(); } catch( CloneNotSupportedException e ) { // DataNotCloneableImplementedクラスは、Cloneableインターフェイス // の実装クラスじゃないのに、Objectクラスのclone()メソッドで // 複製を作ろうとしたので、CloneNotSupportedException例外が投げられます。 e.printStackTrace(); // java.lang.CloneNotSupportedException: DataNotCloneableImplemented // at java.lang.Object.clone(Native Method) // at DataNotCloneableImplemented.clone(Sample.java:170) // at Sample.main(Sample.java:61) } // Objectクラスのclone()メソッドはprotectedなので、 // オーバーライドしないと呼び出せません。 DataNotCloneOverride data = new DataNotCloneOverride(); // data.clone(); // コンパイルエラー: // メソッド clone() は型 Object で不可視です。 } } /** * Objectクラスのclone()メソッドをオーバーライドして、 * 手作業でインスタンスを作りフィールドをコピーする例。 */ class DataNotUseSuper { private int data; public void setData( int i ) { data = i; } public int getData() { return data; } /** * Objectクラスのclone()メソッドをオーバーライドします。 */ protected Object clone() throws CloneNotSupportedException { // 自クラスのインスタンスを作り、フィールドをコピーします。 // (自クラスなので、privateフィールドもアクセスできます) DataNotUseSuper newData = new DataNotUseSuper(); newData.data = this.data; return newData; } } /** * Objectクラスのclone()メソッドをオーバーライドして、 * Objectクラスのclone()メソッドを使用して複製を作ります。 */ class DataUseSuper implements Cloneable { private int data; public void setData( int i ) { data = i; } public int getData() { return data; } /** * Objectクラスのclone()メソッドをオーバーライドします。 */ protected Object clone() throws CloneNotSupportedException { // Objectクラスのclone()メソッドで複製を作ります。 // Cloneableインターフェイスの実装クラスでないと // CloneNotSupportedException例外が投げられます。 return super.clone(); } } /** * Objectクラスのclone()メソッドをオーバーライドして、 * Objectクラスのclone()メソッドを使用して複製を作ります。 * ただし、Cloneableインターフェイスの実装クラスでは * ありません(つまり間違った例です)。 */ class DataNotCloneableImplemented { private int data; public void setData( int i ) { data = i; } public int getData() { return data; } /** * Objectクラスのclone()メソッドをオーバーライドします。 */ protected Object clone() throws CloneNotSupportedException { // Objectクラスのclone()メソッドで複製を作ります。 // でも、Cloneableインターフェイスの実装クラスではないので // CloneNotSupportedException例外が投げられます。 return super.clone(); } } /** * Objectクラスのclone()メソッドをオーバーライドしていいないクラス。 * これも間違った例です。 */ class DataNotCloneOverride { private int data; public void setData( int i ) { data = i; } public int getData() { return data; } }