web-dev-qa-db-ja.com

参照を返さずにオブジェクトを返す

オブジェクトとオブジェクトのリストを返すときに問題が発生します。問題は、開発者の例ではオブジェクトを返したいが、参照は与えたくないということです。これのための最良のアプローチは何ですか?まず、新しい開発者を返すだけだと思っていました。

public Developer getDeveloper(int index){
    Developer d =  developers.get(index);
    return new Developer(d.getName());
}

しかし、これは単純なオブジェクトには良さそうですが、オブジェクトまたはリストでもあるインスタンスフィールドを持つ開発者オブジェクトがある場合はどうなりますか?

他の質問は、開発者のリストを返すリストについてです:

public List<Developer> getDeveloppers(){
    return Collections.unmodifiableList(this.developers);
}

そのため、リストは変更できなくなりましたが、オブジェクトは変更できないと思います...変更できないオブジェクトを含むリストを返すにはどうすればよいですか?

コード例:

import Java.util.ArrayList;
import Java.util.Collections;
import Java.util.List;

public class Project {

    private List<Developer> developers;
    private String name;

    public Project(){
        this("DEFAULT");
    }

    public Project(String name){
        this.setName(name);
        this.developers = new ArrayList<Developer>();
    }

    public List<Developer> getDeveloppers(){
        return Collections.unmodifiableList(this.developers);
    }

    public Developer getDeveloper(int index){
        Developer d =  developers.get(index);
        return new Developer(d.getName());
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }   
}

開発者クラス、これにはインスタンスオブジェクトがないことに注意してください。

public class Developer {

    private String name;

    public Developer(String name){
        this.name = name;
    }

    public String getName(){
        return this.name;
    }
}

助けていただければ幸いです!よろしく

2
John

オブジェクトを不変にすることができます:

_public class Developer {

    private final String name;

    public Developer(String name){
        this.name = name;
    }

    public String getName(){
        return this.name;
    }

}
_

その後、参照を返すことに問題はありません。

それ以外の場合は、各開発者をそれ自体の変更不可能なビューにマップできます。

_public Stream<IDeveloper> getDeveloppers(){
    return this.developers.map(dev => dev.unmoddifiableView());
}
_

unmoddifiableView()は、変更を許可しないIDeveloperのインスタンスを返します。

_private static IDeveloper readOnlyView = new UnmodifiableDeveloper(this);

public IDeveloper unmoddifiableView(){
    return readOnlyView;
}
_

UnmodifiableDeveloperは、すべてのゲッターをコンストラクターパラメーターに委任します。

4
ratchet freak

あなたが説明していることを行う直接的な方法はありません。ただし、希望どおりの結果が得られるオプションがいくつかあります。

  1. ミューテーターではなくアクセサーのみを含むインターフェースを用意し、戻り値の型が具象クラスではなくインターフェースであることを確認してください。
  2. クラスのクローンを作成する方法があります。これは、外部メソッド、またはクラスの.clone()メソッドの可能性があります。これはクラスの新しいインスタンスを返すはずですが、同じ情報を表す状態です(たとえば、nameは同じです)。次に、元のクラスを返す代わりに、コピーを返します。これらは引き続き変更可能ですが、これらのインスタンスを変更しても元のインスタンスには影響しません。
  3. 最初のクラスの不変の「ビュー」を表す2番目のクラスがあります。これは、ラチェットフリークが彼の回答で述べていることです。ここでも、元のクラスのインスタンスからビューのインスタンスを作成できるメソッドが必要です。
  4. これを行う必要がないようにクラスを設計します。

これらがあなたが制御するクラスであると仮定すると、4は私の圧倒的なお気に入りのオプションであり、除外する十分な理由がない限り、間違いなく最初の目的でなければなりません。いくつかの理由:

  • 1、2、および3はすべて、コード(インターフェースまたはメソッド)に煩わしい追加が必要です。特に2と3の場合、これがコード全体で頻繁に行うことである場合、重要なメンテナンス作業を自分で作成していることになります。
  • クラスは、独自のカプセル化を担当する必要があります。クラスをオープンにし、見知らぬ人の優しさに頼って、クラスを保護するための方法を見つけることで、このアイデアを頭に入れています。この責任を常に1つの場所に集中させる代わりに、このクラスを別の場所で使用するたびに、開発者は、使用方法と受け渡し方法に関するこれらの追加ルールを覚えておく必要があります。
  • カプセル化に関する本当に便利な点の1つは、クラスがその状態について仮定を立てることができることです。ただし、これらの仮定をtrueに保つ責任も負います。例として、Fractionクラスがあります。このクラスが独自のカプセル化された状態を管理し、それ自体を縮小形式で保持する場合、そのequalsメソッドは次のようになります。

    _equals(Object obj) {
        return obj.numerator = numerator  && obj.denominator = denominator;
    }
    _

    numeratordenominatorがカプセ​​ル化されていなかった場合、それが縮小された形式であるかどうか確信が持てず、より複雑なロジックを使用して確認する必要があります。たとえば、_1/2_と_2/4_は等しいと比較されます。これはクラス不変と呼ばれます。クラスを使用するたびに、その不変条件を保護することを忘れないようにして、バグと恐らく恐ろしく結合されたコードへの確実なルートになることを期待してください。これは、前の箇条書きの別の側面です。.clone()などを呼び出すことを覚えておくためにこのクラスを使用するすべてのクラスに依存している場合、カプセル化の責任を裏返しにしています。

haveでこれを行う(またはそれを行わない場合のコストが高すぎる)状況に自分がいる場合、他のすべての条件が同じであれば、オプション1をお勧めします。インターフェースの比較的自然な使用。

3
Ben Aaronson