5

オブジェクトをコピーしようとしていますが、元のオブジェクトを変更せずに変更されます。

私はこの解決策を見つけましたが、最良のアプローチはコピーコンストラクターであるように思われました-私の理解では、これによりディープコピー(元のオブジェクトとは完全に別のオブジェクト)が得られます。

だから私はそれを試しました。ただし、次のコードを実行すると、コピー元の以前のすべてのオブジェクトに影響があることに気付きました。surveyCopy.take()内の値を変更するを呼び出すと、 SurveyselectedSurvey 内の値も変更されます。

public class MainDriver {
...
//Code that is supposed to create the copy
case "11":  selectedSurvey = retrieveBlankSurvey(currentSurveys);
            Survey surveyCopy = new Survey(selectedSurvey);
            surveyCopy.take(consoleIO);
            currentSurveys.add(surveyCopy);
            break;
}

そして、ここに私のコピーコンストラクターのコードがあります:

public class Survey implements Serializable
{
    ArrayList<Question> questionList;
    int numQuestions;
    String taker;
    String surveyName;
    boolean isTaken;

    //Copy constructor
    public Survey(Survey incoming)
    {
        this.taker = incoming.getTaker();
        this.numQuestions = incoming.getNumQuestions();
        this.questionList = incoming.getQuestionList();
        this.surveyName = incoming.getSurveyName();
        this.isTaken = incoming.isTaken();
    }
}

では、問題は正確には何ですか?コピーコンストラクターはそのように機能しませんか? コードの書き方が悪いのでしょうか?

4

4 に答える 4

14

これは、コピー コンストラクターの問題です。

this.questionList = incoming.getQuestionList();

それは、リストへの参照をコピーするだけです。両方のオブジェクトは引き続き同じオブジェクトを参照します。

以下を使用できます。

this.questionList = new ArrayList<Question>(incoming.getQuestionList());

元のリストのコピーを作成します - しかし、それ自体が変更可能な場合、これはまだ十分ではありません。Questionその場合、Question完全な分離を実現するには、各オブジェクトのコピーを作成する必要があります。

他のフィールドはプリミティブまたは参照であるため、問題Stringありません (これは不変であり、参照を安全に共有できます)。

于 2012-12-06T14:35:26.907 に答える
8

これ

this.questionList = incoming.getQuestionList();

ほとんどの場合、元のリストへの参照をコピーします(防御的なコピーを提供する可能性があるため、おそらくそう言います)。getQuestionList()おそらく、そのリストの新しいコピーを作成する必要があります。そしておそらく含まれているQuestionオブジェクト。そして、おそらく彼らが参照するものなら何でも。

これがディープ コピーの問題です。これを確実に行うには、可変オブジェクトをすべてコピーする必要があります。オブジェクトが不変(例: String) である場合、それらは変更できないため、元のオブジェクトが変更されないという確信を持って参照できることに注意してください。同じことがプリミティブにも当てはまります。コード ベースの不変性を奨励する正当な理由の 1 つです。

不変クラスを作成できない場合は、防御コピーを作成するようにクラスを記述します。つまり、クライアントがコレクションを要求した場合、コピーを作成して返す必要があります。そうしないと、善意であると思われるクライアントが内部状態を (不注意またはその他の方法で) 変更する可能性があります。

于 2012-12-06T14:34:48.793 に答える
5

ディープコピーを作成する際の問題は、特定のディープコピーコンストラクターも使用していない限り、プリミティブ型ではないものはすべて参照によってコピーされるという事実です。

特定のケースでは、または変数を値で渡すため(実際には参照によって渡されますが、不変であるため問題はありません)boolintまたは変数に問題はありませんが、を渡しています。あなたがするときStringStringArrayList<Question> questionList

this.object = incoming.object

参照をコピーするだけです。したがって、両方の変数がメモリ内の同じオブジェクトを指しているため、それを深くコピーすることはありません。同じ内部値を持つオブジェクトの別のインスタンスを作成する必要があります。そうすれば、確実になりますthis.object = new YourObject(incoming.object)

これは通常、構成ツリーでクラスが複雑になるほど、すべてをコピーするまで変数を深く掘り下げる必要があることを意味します。

于 2012-12-06T14:40:15.640 に答える
0

単純な pojo (ネストされていない) をコピーする必要がある場合。それなら浅いコピーで十分です。

クローンクラス

java.lang.reflect.Field をインポートします。

public class Cloner {
    public static <T> T cloneShallow(T srcEntity, T destEntity){
        try {
            return copy(srcEntity, destEntity);
        }catch (Exception e){
            e.printStackTrace();
        }
        return null;
    }

    private static <T> T copy(T srcEntity, T destEntity) throws IllegalAccessException, InstantiationException {
        if(srcEntity == null){
            return null;
        }

        Class<?> clazz = srcEntity.getClass();

        T newEntity;

        if(destEntity != null){
            newEntity = destEntity;
        }else{
            //create new instance
            newEntity = (T) srcEntity.getClass().newInstance();
        }

        while (clazz != null) {
            copyFields(srcEntity, newEntity, clazz);
            clazz = clazz.getSuperclass();
        }

        return newEntity;
    }

    private static  <T> T copyFields(T entity, T newEntity, Class<?> clazz) throws IllegalAccessException {
        for (Field field : clazz.getDeclaredFields()) {
            field.setAccessible(true);
            field.set(newEntity, field.get(entity));
        }
        return newEntity;
    }
}

電話しよう..

eg.
Apple apple = new Apple();
apple.setColor("Green");

Apple newApple = Cloner.cloneShallow(apple, new Apple());
( or )
Apple newApple = Cloner.cloneShallow(apple, null);
于 2019-06-01T12:54:41.197 に答える