22

In Java specifically, but likely in other languages as well: when would it be useful to have two references to the same object?

Example:

Dog a = new Dog();
Dob b = a;

Is there a situation where this would be useful? Why would this be a preferred solution to using a whenever you want to interact with the object represented by a?

3
  • It is the essence behind the Flyweight pattern.
    – user40980
    Aug 2, 2014 at 18:25
  • @MichaelT Could you elaborate?
    – Bassinator
    Aug 2, 2014 at 18:27
  • 8
    If a and b always reference the same Dog, then there is no point to it. If they sometimes might, then it is quite useful.
    – Gabe
    Aug 3, 2014 at 2:18

11 Answers 11

47

An example is when you want to have the same object in two separate lists:

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

Another use is when you have the same object playing several roles:

Person p = new Person("Bruce Wayne");
Person batman = p;
Person ceoOfWayneIndustries = p;
7
  • 5
    I am sorry but I believe that for your Bruce Wayne example there is a design flaw, batman and ceo position should be roles for your person. Aug 4, 2014 at 4:06
  • 48
    -1 for revealing Batman's secret identity.
    – Ampt
    Aug 4, 2014 at 15:15
  • 3
    @Silviu Burcea - I strongly agree the Bruce Wayne example is not a good example. On the one hand, if a global text edit changed the name 'ceoOfWayneIndustries' and 'batman' to 'p' (assuming no clashes of name or changes of scope), and the semantics of the program changed, then their is something broken. The object referenced represent the real meaning, not the variable name within the program. To have different semantics, it is either a different object, or is referenced by something with more behaviour than a reference (which should be transparent), and so isn't an example of 2+ references.
    – gbulmer
    Aug 4, 2014 at 15:32
  • 2
    Although the Bruce Wayne example as written might not work, I believe the stated intention is correct. Perhaps a closer example might be Persona batman = new Persona("Batman"); Persona bruce = new Persona("Bruce Wayne"); Persona currentPersona = batman; - where you have multiple possible values (or a list of available values) and a reference to the currently-active/selected one.
    – Dan Puzey
    Aug 6, 2014 at 10:38
  • 1
    @gbulmer: I would posit that you can't have a reference to two objects; the reference currentPersona points to one object or the other, but never both. In particular, it might easily be possible that currentPersona is never set to bruce, in which case it's certainly not a reference to two objects. I'd say that, in my example, both batman and currentPersona are references to the same instance, but serve different semantic meaning within the program.
    – Dan Puzey
    Aug 8, 2014 at 8:33
16

That's actually a surprisingly profound question! Experience from modern C++ (and languages that take from modern C++, such as Rust) suggests that very often, you don't want that! For most data, you want a single or unique ("owning") reference. This idea is also the one main reason for linear type systems.

However, even then you typically want some short-lived "borrowed" references that are used to access the memory briefly but don't last for the a significant fraction of the time the data exists. Most commonly when you pass an object as argument to a different function (Parameters are variables too!):

void encounter(Dog a) {
  hissAt(a);
}

void hissAt(Dog b) {
  // ...
}

A less common case when you use one of two objects depending on a condition, doing essentially the same thing regardless of which you choose:

Dog a, b;
Dog goodBoy = whoseAGoodBoy ? a : b;
feed(goodBoy);
walk(goodBoy);
pet(goodBoy);

Going back to more common uses, but leaving local variables behind, we turn to fields: For example, widgets in GUI frameworks often have parent widgets, so your big frame containing ten buttons would have at least ten references pointing at it (plus some more from its parent and perhaps from event listeners and so on). Any kind of object graph, and some kinds of object trees (those with parent/sibling references), have multiple objects refer to each the same object. And virtually every data set is actually a graph ;-)

4
  • 3
    The "less common case" is quite common: you have the objects stored in a list or a map, retrieve the one you want, and perform the operations required. You do not erase their references from the map or the list.
    – SJuan76
    Aug 2, 2014 at 18:57
  • 1
    @SJuan76 The "less common case" is solely about taking from local variables, anything involving data structures falls under the last point.
    – user7043
    Aug 2, 2014 at 18:59
  • Is this ideal of only one reference due to reference-counting memory management? If so, it would be worth mentioning that the motivation isn't relevant to other languages with different garbage collectors (e.g. C#, Java, Python)
    – MarkJ
    Aug 6, 2014 at 19:50
  • @MarkJ Linear types aren't just an ideal, much existing code unknowingly conforms to it because it's the semantically correct thing for quite a few cases. It does have potential performance advantages (but neither for reference counting nor tracing GCs, it only helps when you use a different strategy that actually exploits that knowledge to omit both refcounts and tracing). More interesting is its application to simple, deterministic resource management. Think RAII and C++11 move semantics, but better (applies more often and mistakes are caught by the compiler).
    – user7043
    Aug 6, 2014 at 19:57
6

Temporary variables: consider the following pseudocode.

Object getMaximum(Collection objects) {
  Object max = null;
  for (Object candidate IN objects) {
    if ((max is null) OR (candidate > max)) {
      max = candidate;
    }
  }
  return max;
}

The variables max and candidate may point to the same object, but the variable assignment changes using different rules and at different times.

3

To supplement the other answers, you may also want to traverse a data structure differently, starting from the same place. For example, if you had a BinaryTree a = new BinaryTree(...); BinaryTree b = a, you could traverse down the tree's leftmost path with a and its rightmost path with b, using something like:

while (!a.equals(null) && !b.equals(null)) {
    a = a.left();
    b = b.right();
}

It's been a while since I've written Java, so that code may not be correct or sensible. Take it more as pseudocode.

3

This method is great when you have several objects that all call back to another object that can be used uncontextually.

For instance, if you have a tabbed interface you may have Tab1, Tab2, and Tab3. You may also want to be able to use a common variable regardless of which tab the user is on to simplify your code and reduce having to figure out on the fly over and over which tab your user is on.

Tab Tab1 = new Tab();
Tab Tab2 = new Tab();
Tab Tab3 = new Tab();
Tab CurrentTab = new Tab();

Then, in each of the numbered tabs onClick, you could change CurrentTab to reference that Tab.

CurrentTab = Tab3;

Now in your code you can call "CurrentTab" with impunity without needing to know which Tab you are actually on. You can also update properties of CurrentTab and they will automatically flow down to the referenced Tab.

3

There are plenty of scenarios wherein b must be a reference to an unknown "a" in order to be useful. In particular:

  • Any time you don't know what b points to at compile-time.
  • Any time you need to iterate over a collection, whether known at compile time or not
  • Any time you have limited scope

For example:

Parameters

public void DoSomething(Thing &t) {
}

t is a reference to a variable from an outside scope.

Return values and other conditional values

Thing a = Thing.Get("a");
Thing b = Thing.Get("b");
Thing biggerThing = Thing.max(a, b);
Thing z = a.IsMoreZThan(b) ? a : b;

biggerThing and z are each references to either a or b. We don't know which at compile-time.

Lambdas and their return values

Thing t = someCollection.FirstOrDefault(x => x.whatever > 123);

x is a parameter (example 1 above), and t is a return value (example 2 above)

Collections

indexByName.add(t.name, t);
process(indexByName["some name"]);

index["some name"] is, to a large extent, a more sophisticated looking b. It's an alias to an object that was created and stuffed into the collection.

Loops

foreach (Thing t in things) {
 /* `t` is a reference to a thing in a collection */
}

t is a reference to an item returned (example 2) by an iterator (previous example).

2
  • Your examples are hard to follow. Don't get me wrong, I got through them, but I had to work at it. In the future, I suggest separating your code examples into individual blocks (with some notes specifically explaining each), not putting a few unrelated examples in one block.
    – Bassinator
    Aug 2, 2014 at 23:06
  • 1
    @HCBPshenanigans I don't expect you to change selected answers; but, I've updated mine to hopefully help readability and fill in some of the use-cases missing from the selected answer.
    – svidgen
    Aug 7, 2014 at 20:56
2

It is a crucial point, but IMHO is worth understanding.

All OO languages always make copies of references, and never copy an object 'invisibly'. It would be much harder to write programs if OO languages worked any other way. For example, functions, and methods, could never update an object. Java, and most OO languages would be almost impossible to use without significant added complexity.

An object in a program is supposed to have some meaning. For example it represents something specific in the real physical world. It usually makes sense to have many references to the same thing. For example, my home address can be given to many people and organisations, and that address always refers to the same physical location. So the first point is, objects often represent something which is specific, real, or concrete; and so being able to have many references to the same thing is extremely useful. Otherwise it would be harder to write programs.

Every time you pass a as an argument/parameter to another function e.g. calling
foo(Dog aDoggy);
or apply a method to a, the underlying program code makes a copy of the reference, to produce a second reference to the same object.

Further, if code with a copied reference is in a different thread, then both can be used concurrently to access the same object.

So in most useful programs, there will be multiple references to the same object, because that is the semantics of most OO programming languages.

Now, if we think about it, because passing by reference is the only mechanism available in many OO languages (C++ supports both), we might expect it to be the 'right' default behaviour.

IMHO, using references is the right default, for a couple of reasons:

  1. It guarantees that the value of an object used in two different places is the same. Imagine putting an object into two different data structures (arrays, lists etc.), and doing some operations on an object that changes it. That could be a nightmare to debug. More importantly, it is the same object in both data structures, or the program has a bug.
  2. You can happily refactor code into several functions, or merge the code from several functions into one, and the semantics do not change. If the language did not provide reference semantics, it would be even more complex to modify code.

There is also an efficiency argument; making copies of entire objects is less efficient than copying a reference. However, I think that misses the point. Multiple references to the same object make more sense, and are easier to use, because they match the semantics of the real physical world.

So, IMHO, it usually makes sense to have multiple references to the same object. In the unusual cases where that doesn't make sense in the context of an algorithm, most languages provide the ability to make a 'clone' or deep copy. However that is not the default.

I think people who argue that this should not be the default are using a language which does not provide automatic garbage collection. For example, old fashioned C++. The issue there is that they need to find a way to collect 'dead' objects and not reclaim objects that may still be required; having multiple references to the same object makes that hard.

I think, if C++ had sufficiently low-cost garbage collection, so that all referenced objects are garbage collected, then much of the objection goes away. There will still be some cases where reference semantics is not what is needed. However, in my experience, the people who can identify those situations are also usually capable of choosing the appropriate semantics anyway.

I believe there is some evidence that a large amount of the code in a C++ program is there to handle or mitigate garbage collection. However, writing, and maintaining that sort of 'infrastructural' code adds cost; it is there to make the language easier to use, or more robust. So, for example the Go language is designed with a focus on remediating some of the weaknesses of C++, and it has no choice except garbage collection.

This is of course irrelevant in the context of Java. It too was designed to be easy to use, and so has garbage collection. Hence having multiple references is the default semantics, and is relatively safe in the sense that objects aren't reclaimed while there is a reference to them. Of course they might be held onto by a data structure because the program doesn't properly tidy-up when it has really finished with an object.

So, circling back around to your question (with a bit of generalisation), when would you want more than one references to the same object? Pretty much in every situation I can think of. They are the default semantics of most languages parameter passing mechanism. I suggest that is because the default semantics of handling objects which exists in the real world pretty much has to be by reference ('cos the actual objects are out there).

Any other semantics would be harder to handle.

Dog a = new Dog("rover");  // initialise with name 
DogList dl = new DogList()
dl.add(a)
...
a.setOwner("Mr Been")

I suggest that the "rover" in dl should be the one effected by setOwner or programs get hard to write, understand, debug or modify. I think most programmers would be puzzled or dismayed otherwise.

later, the dog is sold:

soldDog = dl.lookupOwner("rover", "Mr Been")
soldDog.setOwner("Mr Mcgoo")

This sort of processing is common and normal. So reference semantics are the default because it usually makes most sense.

Summary: It always makes sense to have multiple references to the same object.

2
  • good point, but this is better suited as a comment
    – Bassinator
    Aug 4, 2014 at 0:24
  • @HCBPshenanigans - I think the point might have been too terse, and left too much unsaid. So I have expand on the reasoning. I think it is a 'meta' answer to your question. Summary, multiple references to the same object are crucial to making programs easy to write because many objects in a program represent specific or unique instances of things in the real world.
    – gbulmer
    Aug 4, 2014 at 14:45
1

Of course, one other scenario where you might end up with:

Dog a = new Dog();
Dog b = a;

is when you're maintaining code and b used to be a different dog, or a different class, but now is serviced by a.

Generally, in the medium term, you should rework all the code to refer to a directly, but that may not happen straight away.

1

You'd want this anytime your program has a chance of pulling an entity into memory in more than one spot, possibly because different components are using it.

Identity Maps provided a definitive local store of entities so that we can avoid having two or more separate representations. When we represent the same object twice, our client runs the risk of causing a concurrency issue if one reference of the object persists its state changes before the other other instance does. The idea is we want to ensure that our client is always dealing the definitive reference to our entity/object.

0

I used this when writing a Sudoku solver. When I know the number of a cell when processing rows, I want the containing column to know that cell's number too when processing columns. So both columns and rows are arrays of Cell objects that overlap. Exactly like the accepted answer showed.

-2

In web applications, Object Relational Mappers can use lazy loading so that all references to the same database object (at least within the same thread) point to the same thing.

For instance, if you had two tables:

Dogs:

  • id | owner_id | name
  • 1 | 1 | Bark Kent

Owners:

  • id | name
  • 1 | me
  • 2 | you

There are several approaches that your ORM can do if the following calls are made:

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

In the naive strategy, all calls to the models and the related references retrieve separate objects. Dog.find(), Owner.find(), owner.dogs, and dog.owner result in a database hit the first time around, after which they are saved to memory. And so:

  • database is fetched from at least 4 times
  • dog.owner is not the same as superdog.owner (fetched separately)
  • dog.name is not the same as superdog.name
  • dog and superdog both attempt to write to the same row and will overwrite each other's results: write3 will undo the name change in write1.

Without a reference, you have more fetches, use more memory, and introduce the possibility of overwriting earlier updates.

Suppose that your ORM knows that all references to row 1 of the dogs table should point to the same thing. Then:

  • fetch4 can be eliminated as there is an object in memory corresponding to Owner.find(1). fetch3 will still result in at least an index scan, as there might be other dogs owned by owner, but won't trigger a row retrieval.
  • dog and superdog point to the same object.
  • dog.name and superdog.name point to the same object.
  • dog.owner and superdog.owner point to the same object.
  • write3 does not overwrite the change in write1.

In other words, using references helps codify the principle of a single point of truth (at least within that thread) being the row in the database.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge that you have read and understand our privacy policy and code of conduct.

Not the answer you're looking for? Browse other questions tagged or ask your own question.