0

Consider the two classes:

public class Point {
    public int x;
    public int y;

    public Point(int xVal, int yVal) {
        x = xVal;
        y = yVal;
    }

    public Point(Point pt) {
        x = pt.x;
        y = pt.y;
    }
}

public class BoundingBox {
    public Point topLeft;
    public Point bottomRight;

    public BoundingBox(Point setTopLeft, Point setBottomRight) {
        topLeft = new Point(setTopLeft);
        bottomRight = new Point(setBottomRight);
    }
}

Should BoundingBox make a copy of the points passed into its constructor as shown, or just take a reference to them? If it assumes their reference values, is it guaranteed that those Point objects will exist as long as the BoundingBox exists?

William the Coderer
  • 708
  • 1
  • 7
  • 19
  • 1
    I think only primitive types will be passed by value.Others will be passed by reference. – sathis Nov 08 '11 at 05:36
  • I know, but I need to know whether to copy the values in those objects (clone it to another new object), or to keep their reference and keep using them – William the Coderer Nov 08 '11 at 05:38
  • although it is very related to this issue, and the argue about 'pass by reference' and 'pass by value' is really tedious, but i'd like to say there is no such thing as 'pass by reference' in java. Have a look at: http://stackoverflow.com/questions/7301637/pass-by-value-or-pass-by-reference-in-java – James.Xu Nov 08 '11 at 05:44
  • yes, I like to think of it as an immutable reference. the object will continue to exist in its own right, not dependent on the variable. – William the Coderer Nov 08 '11 at 05:49

5 Answers5

3

two questions:

  1. make a copy or not? It depends, if the point will be shared -- be used by other threads, then make a copy is better; If not shared, then just use reference may got tiny performance gain.

  2. Is it guaranteed that those Point objects will exist as long as the BoundingBox exists? yes, it is guaranteed by the JVM. since the points are referenced by the BoundingBox, they will not be garbage collected.

James.Xu
  • 8,249
  • 5
  • 25
  • 36
  • Thanks. That's what I needed to know, since my objects are being used in a multi-threaded app. I figured it might keep some kind of reference count for garbage collection, but I wasn't sure. And the threads that created the Point objects may cease to exist. – William the Coderer Nov 08 '11 at 05:42
  • 1
    The lifetime of an Object does not bound to the Thread which created it. Whether an object is garbage-collected or not only depends on whether it is referenced. – James.Xu Nov 08 '11 at 05:46
3

You should always make defensive copies of stored mutable objects in your classes. You should always program defensively, assume that everyone that uses your class will break it.

This is demonstrated by the fact that although the class itself might be immutable, doesn't mean that objects in it are immutable too. You need to make defensive copies of mutable objects that you are using within your class.

Here's an example:

    public class MyClass {
           private Point foo;
           private Point bar;

           public MyClass(Point foo, Point bar) {
             this.foo = foo;
             this.bar = bar;
           }

           public Point foo() {
             return foo;
           }

           public Point bar() {
             return bar;
           }

           . . .

           //Seems harmless enough?
           //Lets destroy it
           Point foo = new Point(1 ,2);
           Point bar = new Point(3 ,4);
           MyClass mc = new MyClass(foo, bar);
           bar.x = 99; //<-- changes internal of mc!

This happens because the object MyClass only ever stored a pointer to the Point object that was passed in to it, which means that when the mutable object is changed - anything that points to that object is also changed. This can cause unintentional and unexepected results

To repair the above code, you make defensie copies of everything. It is important to note that copies should be made before any parameter checking occurs (e.g. validity check). You also need to make sure that your accessors are changed so that they too return copies of the internals of the class.

         //Fixed version!
            public MyClass(Point foo, Point bar) {
             this.foo = new Point(foo.getLocation());
             this.bar = new Point(bar.getLocation());
           }

           public Point foo() {
             return new Point(foo.getLocation());
           }

           public Point bar() {
             return new Point(bar.getLocation());
           }

           . . .

For the second part of your question - as long as BoundingBox exists, the objects contained within it should exist too. The JVM wont garbage collect those until all references to them no longer exist.

Deco
  • 3,261
  • 17
  • 25
2

If you can make the Point object immutable, then you should be fine taking references just to the points. You can do this by declaring x, y fields as final as below:

public class Point {
    public final int x;
    public final int y;

    public Point(int xVal, int yVal) {
        x = xVal;
        y = yVal;
    }

    public Point(Point pt) {
        x = pt.x;
        y = pt.y;
    }
}

Holding references onto mutable objects would mean that a BoundingBox instance may just change it's dimensions if a topLeft, topRight reference were changed by any other class - leading to bugs that would be very hard to debug.

Also, Java reference counts objects, and they are not destroyed until all the references have been released. So, the points in your BoundingBox will be just fine as long as there are references to them.

inder
  • 943
  • 11
  • 15
  • That is very helpful, too. Never thought of that. My brain seems to think of everything but the plainly obvious, although I am more of a C programmer (functional languages). The objects being created will not be changed elsewhere, so they could very well be made immutable. Might consider that. – William the Coderer Nov 08 '11 at 05:57
1

If it assumes their reference values, is it guaranteed that those Point objects will exist as long as the BoundingBox exists?

Yes, the point objects will continue to exist until they're not used anymore. This is what the JVM garbage collection does for you.

Should BoundingBox make a copy of the points passed into its constructor as shown, or just take a reference to them?

You should make a copy of them. Otherwise, what happens when someone else comes along and does this:

Point topLeft = new Point(1, 2);
Point bottomRight = new Point(3, 4);
BoundingBox box = new BoundingBox(topLeft, bottomRight);

topLeft.x = 5; // Oops, this just changed box.topLeft.x

Generally it's good practice to avoid "gotchas" -- code that works in ways that's not expected. Even if you remember what your code's gotchas are now, the first time you forget one you're going to be really confused.

Brendan Long
  • 53,280
  • 21
  • 146
  • 188
  • wow, I can't believe I didn't think of the obvious... thankfully you brought that up. very good point indeed. – William the Coderer Nov 08 '11 at 05:43
  • On the other hand, you could make Point immutable (by making x and y 'final'), which would avoid that gotcha just like Java's 'String' class does. – Dolda2000 Nov 08 '11 at 06:07
1

Neither way is right or wrong. It all depends on the specifics of your application. If your bounding box needs to rely on the points not to change from under it, then you have to make a copy in the constructor. But then you also need to make sure to only publish copies of the objects in any getter methods (e.g. if you end up having getTopLeft(), getBottomRight(), etc. More generally, this is a question of whether use composition vs. aggregation when designing your object model. Aggregation doesn't imply ownership. In other words, aggregated objects can exist outside the scope of their parent like in the case where you simply store the same point references that where passed to the constructor. With object composition, the life span of the child objects will be the same as that of the parent object. This is achieved by making copies of the points and then being careful about not passing out references of the stored points (only copies). This way, once the parent object goes away, so will the children.

Dmitry B.
  • 9,107
  • 3
  • 43
  • 64