回答:
例は、2つの別々のリストに同じオブジェクトを持ちたい場合です:
Dog myDog = new Dog();
List dogsWithRabies = new ArrayList();
List dogsThatCanPlayPiano = new ArrayList();
dogsWithRabies.add(myDog);
dogsThatCanPlayPiano.add(myDog);
// Now each List has a reference to the same dog
別の用途は、同じオブジェクトがいくつかの役割を果たしている場合です。
Person p = new Person("Bruce Wayne");
Person batman = p;
Person ceoOfWayneIndustries = p;
Persona batman = new Persona("Batman"); Persona bruce = new Persona("Bruce Wayne"); Persona currentPersona = batman;
複数の可能な値(または使用可能な値のリスト)と、現在アクティブ/選択されている値への参照がある場合があります。
currentPersona
は一方のオブジェクトまたは他方を指しますが、両方を指すことはありません。特に、簡単にその可能性がありますcurrentPersona
されて決してに設定されていないbruce
、それは確かに二つのオブジェクトへの参照ではありません、その場合には、。私の例では、batman
とcurrentPersona
は両方とも同じインスタンスへの参照ですが、プログラム内で異なる意味上の意味を持っています。
それは実際、驚くほど深遠な質問です!最新のC ++(およびRustなどの最新のC ++から取った言語)の経験から、これは望ましくないことが非常に多いことがわかります。ほとんどのデータでは、単一または一意の(「所有」)参照が必要です。この考え方は、線形型システムの 1つの主な理由でもあります。
ただし、その場合でも、通常、メモリに短時間アクセスするために使用されるが、データが存在する時間のかなりの部分が持続しない、短命の「借用」参照が必要です。最も一般的には、オブジェクトを別の関数に引数として渡すとき(パラメーターも変数です!):
void encounter(Dog a) {
hissAt(a);
}
void hissAt(Dog b) {
// ...
}
条件に応じて2つのオブジェクトのいずれかを使用し、どちらを選択しても本質的に同じことを行う場合は、あまり一般的ではありません。
Dog a, b;
Dog goodBoy = whoseAGoodBoy ? a : b;
feed(goodBoy);
walk(goodBoy);
pet(goodBoy);
より一般的な用途に戻って、ローカル変数を残して、フィールドに目を向けます:たとえば、GUIフレームワークのウィジェットには多くの場合親ウィジェットがあるため、10個のボタンを含む大きなフレームには少なくとも10個の参照があります(さらにその親から、そしておそらくイベントリスナーなどから)。あらゆる種類のオブジェクトグラフ、およびいくつかの種類のオブジェクトツリー(親/兄弟参照を持つもの)には、それぞれが同じオブジェクトを参照する複数のオブジェクトがあります。そして、事実上すべてのデータセットは実際にはグラフです;-)
一時変数:次の擬似コードを検討してください。
Object getMaximum(Collection objects) {
Object max = null;
for (Object candidate IN objects) {
if ((max is null) OR (candidate > max)) {
max = candidate;
}
}
return max;
}
変数max
とcandidate
は同じオブジェクトを指す場合がありますが、変数の割り当ては異なるルールを使用して、異なる時間に変更されます。
他の回答を補足するために、同じ場所から始めて、データ構造を異なる方法でトラバースすることもできます。たとえば、を持っている場合、次のようなものを使用BinaryTree a = new BinaryTree(...); BinaryTree b = a
して、ツリーの左端のパスとa
を使用して右端のパスをトラバースできますb
。
while (!a.equals(null) && !b.equals(null)) {
a = a.left();
b = b.right();
}
Javaを書いてからしばらく経ちました。そのため、コードが正しくない、または賢明ではないかもしれません。擬似コードとしてより多くを取ります。
このメソッドは、すべてがコンテキストに関係なく使用できる別のオブジェクトにコールバックする複数のオブジェクトがある場合に便利です。
たとえば、タブ付きインターフェイスがある場合、Tab1、Tab2、およびTab3があります。また、ユーザーがどのタブを使用しているかに関係なく共通変数を使用して、コードを簡素化し、ユーザーがどのタブを使用しているかをその場で把握する必要性を減らすこともできます。
Tab Tab1 = new Tab();
Tab Tab2 = new Tab();
Tab Tab3 = new Tab();
Tab CurrentTab = new Tab();
次に、番号の付いた各タブonClickで、そのタブを参照するようにCurrentTabを変更できます。
CurrentTab = Tab3;
これで、コード内で実際にどのタブを使用しているかを知る必要なく、「CurrentTab」を免責で呼び出すことができます。CurrentTabのプロパティを更新することもできます。これらのプロパティは、参照されているタブに自動的に流れます。
有用であるためには未知の「」への参照でb
なければならない多くのシナリオがありますa
。特に:
b
コンパイル時に何を指しているのかわからないときはいつでも。例えば:
パラメーター
public void DoSomething(Thing &t) {
}
t
外部スコープからの変数への参照です。
戻り値と他の条件値
Thing a = Thing.Get("a");
Thing b = Thing.Get("b");
Thing biggerThing = Thing.max(a, b);
Thing z = a.IsMoreZThan(b) ? a : b;
biggerThing
およびz
は、a
またはへの参照b
です。コンパイル時にどれがわからない。
ラムダとその戻り値
Thing t = someCollection.FirstOrDefault(x => x.whatever > 123);
x
パラメータ(上記の例1)、およびt
戻り値(上記の例2)
コレクション
indexByName.add(t.name, t);
process(indexByName["some name"]);
index["some name"]
大部分は、より洗練された見た目b
です。これは、作成されてコレクションに詰め込まれたオブジェクトのエイリアスです。
ループ
foreach (Thing t in things) {
/* `t` is a reference to a thing in a collection */
}
t
イテレータ(前の例)によって返されたアイテム(例2)への参照です。
それは重要なポイントですが、私見は理解する価値があります。
すべてのOO言語は常に参照のコピーを作成し、「目に見えない」オブジェクトを決してコピーしません。オブジェクト指向言語が他の方法で機能する場合、プログラムを書くことははるかに困難です。たとえば、関数やメソッドはオブジェクトを更新できません。Java、およびほとんどのオブジェクト指向言語は、大幅に複雑さを増すことなく使用することはほとんど不可能です。
プログラム内のオブジェクトには何らかの意味があるはずです。たとえば、実際の物理的な世界に固有の何かを表します。通常、同じものへの多くの参照を持つことは理にかなっています。たとえば、私の自宅の住所は多くの人々や組織に与えることができ、その住所は常に同じ物理的な場所を指します。そのため、最初のポイントは、オブジェクトは多くの場合、具体的、現実的、または具体的なものを表します。そのため、同じものへの多くの参照を持つことができると非常に便利です。そうしないと、プログラムを作成するのが難しくなります。
a
引数やパラメータとして別の関数に
foo(Dog aDoggy);
メソッドを呼び出したり適用したりするたびa
に、基になるプログラムコードは参照のコピーを作成して、同じオブジェクトへの2番目の参照を生成します。
さらに、参照がコピーされたコードが別のスレッドにある場合、両方を同時に使用して同じオブジェクトにアクセスできます。
したがって、ほとんどの有用なプログラムでは、同じオブジェクトへの複数の参照があります。これは、ほとんどのオブジェクト指向プログラミング言語のセマンティクスであるためです。
考えてみると、参照渡しは多くのOO言語で利用可能な唯一のメカニズムであるため(C ++は両方をサポートします)、「正しい」デフォルトの動作であると予想されるかもしれません。
私見、参照を使用することは、いくつかの理由から正しいデフォルトです:
効率性の議論もあります。オブジェクト全体のコピーの作成は、参照のコピーよりも効率的ではありません。しかし、その点を見逃していると思います。同じオブジェクトへの複数の参照は、実際の物理世界のセマンティクスと一致するため、より意味があり、使いやすいです。
だから、私見、通常は同じオブジェクトへの複数の参照を持つことが理にかなっています。アルゴリズムの文脈でそれが意味をなさない異常なケースでは、ほとんどの言語は「クローン」またはディープコピーを作成する機能を提供します。ただし、これはデフォルトではありません。
これをデフォルトにするべきではないと主張する人々は、自動ガベージコレクションを提供しない言語を使用していると思います。たとえば、昔ながらのC ++。そこに問題があるのは、彼らが「死んだ」オブジェクトを収集する方法を見つける必要があり、まだ必要なオブジェクトを回収しないことです。同じオブジェクトへの複数の参照があると、それは難しくなります。
C ++に十分な低コストのガベージコレクションがあり、参照されるすべてのオブジェクトがガベージコレクションされると、異議の多くはなくなると思います。参照セマンティクスが必要なものではない場合もあります。ただし、私の経験では、これらの状況を特定できる人は通常、とにかく適切なセマンティクスを選択することもできます。
ガベージコレクションを処理または軽減するために、C ++プログラムの大量のコードが存在するという証拠があると思います。ただし、そのような「インフラストラクチャ」コードを作成および維持するには、コストがかかります。言語を使いやすくするか、より堅牢にするためにあります。したがって、たとえばGo言語は、C ++の弱点のいくつかを修正することに重点を置いて設計されており、ガベージコレクション以外には選択肢がありません。
もちろん、これはJavaのコンテキストでは無関係です。これも使いやすいように設計されているため、ガベージコレクションもあります。したがって、複数の参照を持つことはデフォルトのセマンティクスであり、オブジェクトへの参照がある間はオブジェクトが回収されないという意味で比較的安全です。もちろん、オブジェクトで実際に終了したときにプログラムが適切に整理されないため、データ構造によって保持される可能性があります。
だから、あなたの質問に戻って(少し一般化して)、同じオブジェクトへの複数の参照が必要になるのはいつですか?私が考えることができるあらゆる状況でほとんど。これらは、ほとんどの言語のパラメーターを渡すメカニズムのデフォルトのセマンティクスです。それは、現実の世界に存在するオブジェクトを処理するデフォルトのセマンティクスは、参照によるものでなければならないからです(実際のオブジェクトはそこにあるので)。
他のセマンティクスは処理が難しくなります。
Dog a = new Dog("rover"); // initialise with name
DogList dl = new DogList()
dl.add(a)
...
a.setOwner("Mr Been")
「ローバー」はdl
影響を受けるsetOwner
プログラムであるか、プログラムの作成、理解、デバッグ、変更が困難になることをお勧めします。そうでなければ、ほとんどのプログラマーは困惑したり、落胆したりすると思います。
後で、犬は売られます:
soldDog = dl.lookupOwner("rover", "Mr Been")
soldDog.setOwner("Mr Mcgoo")
この種の処理は一般的かつ正常です。したがって、参照セマンティクスは通常最も意味があるため、デフォルトです。
要約:同じオブジェクトへの複数の参照を持つことは常に意味があります。
おそらく異なるコンポーネントが使用しているために、プログラムがエンティティを複数の場所でメモリにプルする可能性があるときはいつでもこれが必要です。
アイデンティティマップは、エンティティの明確なローカルストアを提供するため、2つ以上の別個の表現を避けることができます。同じオブジェクトを2回表す場合、クライアントは、オブジェクトの1つの参照が他のインスタンスよりも先に状態の変更を保持する場合、同時実行の問題を引き起こすリスクを負います。アイデアは、クライアントが常にエンティティ/オブジェクトへの最終的な参照を処理していることを確認することです。
Webアプリケーションでは、オブジェクトリレーショナルマッパーは遅延読み込みを使用して、同じデータベースオブジェクト(少なくとも同じスレッド内)へのすべての参照が同じものを指すようにすることができます。
たとえば、2つのテーブルがある場合:
以下の呼び出しが行われた場合、ORMが実行できるいくつかのアプローチがあります。
dog = Dog.find(1) // fetch1
owner = Owner.find(1) // fetch2
superdog = owner.dogs.first() // fetch3
superdog.name = "Superdog"
superdog.save! // write1
owner = dog.owner // fetch4
owner.name = "Mark"
owner.save! // write2
dog.owner = Owner.find(2)
dog.save! // write3
単純な戦略では、モデルと関連する参照へのすべての呼び出しが個別のオブジェクトを取得します。Dog.find()
、Owner.find()
、owner.dogs
、およびdog.owner
データベースになり、それらがメモリに保存された後、最初の頃にヒット。など:
参照がなければ、より多くのフェッチがあり、より多くのメモリを使用し、以前の更新を上書きする可能性があります。
ORMがdogsテーブルの行1へのすべての参照が同じものを指している必要があることを知っているとします。次に:
つまり、参照を使用すると、データベース内の行である単一の真実(少なくともそのスレッド内)の原則を体系化するのに役立ちます。