2

Consider this code:

package Prova;

import java.util.ArrayList;

public class Prova
{
    private ArrayList<String> people;
    public Prova() {
        people=new ArrayList<String> ();
    }

    public ArrayList<String> getPeople (){
        return people;
    }

    public static void main(String[] args) {
        Prova p=new Prova();
        p.go();
    }

    public void go(){
        ArrayList<String> temp=getPeople();
        temp.add("jack");
        System.out.print(getPeople());
    }
}

It prints "jack".

Why? Doesn't this violate encapsulation? How to return it by value?

Nathaniel Ford
  • 20,545
  • 20
  • 91
  • 102
Ramy Al Zuhouri
  • 21,580
  • 26
  • 105
  • 187
  • Java is always pass by reference when it comes to objects. What your code does just verify that fact. So what exactly are you expecting to return? – Churk Mar 31 '12 at 12:36
  • 2
    @Churk [Java is not pass-by-reference](http://stackoverflow.com/q/40480/395760)! If it was pass-by-reference, `getPeople() = ...` would be possible and change the member to point to another ArrayList. For argument passing, analogous counter-examples exist and are given by the dozen in the question I linked to (not to mention the dozens of duplicates). –  Mar 31 '12 at 12:39
  • I just want to reiterate @delnan 's point. In Java, a reference (fundamentally) is *passing the value of the pointer to the object* – Chris Dargis Jul 17 '12 at 03:02

5 Answers5

10

You need to program defensively. There are a few alternatives to consider

  • Don't expose the list externally, expose methods to apply to the list instead, e.g.,
public void addPerson(String personName) {
    people.add(personName);
}
  • Return immutable objects or a copy of the object. E.g.,
public List<String> getPeople {
     return new ArrayList<String>(people);
}

As far as the why goes, it's as already explained by other posts. The value to the reference of the ArrayList is passed (alas changing the value doesn't change the original reference). However the list itself contains modifiable references to its objects.

Johan Sjöberg
  • 47,929
  • 21
  • 130
  • 148
3

Java is always pass by value:

  1. For primitive type, it passes value directly.
  2. For object it passes value of the object reference.

So for your case, it is passing value of the reference object. Thus object reference.

Kowser
  • 8,123
  • 7
  • 40
  • 63
1

The getPeople() method is violating encapsulation here because it returns a reference to its private list instead of returning an immutable view or a copy. You could easily solve this by implementing this method as:

public List<String> getPeople() {
    return Collections.unmodifiableList(people);
}

I recommend to have a look at Joshua Bloch's excellent book "Effective Java (2nd Edition)", "Item 39: Make defensive copies when needed".

Christian Schlichtherle
  • 3,125
  • 1
  • 23
  • 47
1

Immutability is charming but as with making defensive copies, it can become expensive. The standard java collections just aren't designed as immutable data structures. So it would be nice if you can go like Johan Sjöberg suggests and do not expose the list at all.

But you should also consider why you need to enforce such a high level of encapsulation. Are you exposing your class as a public API? If not, if you know and control the clients of your class well, too much encapsulation can be just impractical. Remember that encapsulation/information hiding is less about security but more about presenting a concise and unambiguous API to a client.

nansen
  • 2,912
  • 1
  • 20
  • 33
  • Collections.unmodifiableList(people) is not expensive at all - it's just a simple wrapper. – Christian Schlichtherle Apr 01 '12 at 06:38
  • Yes but it is a view of the original collection. So modifying the original will reflect in the view. You should be sure that that's what you want. I mean it is very different from making defensive copies. – nansen Apr 01 '12 at 10:27
0

Java is essentially "pass by value", with a small but significant difference from what is usually meant by that i.e. it acutually is "pass by reference value". So when you deal with Java Types(the Class citizens of the JVM, the non-primitive types), declaration like the following basically means that you will get a copy of the reference referenceToMyObject of Type MyClass, pointing to the same specific MyClass Object instance in the JVM heap memory.

public class SomeClass {

    private MyClass referenceToMyClassInstance = new MyClass("instanceId-1");

    public MyClass getMyClassInstance() {
          return referenceToMyClassInstance;
    }

}

So in your example you basically get copy of reference pointing to the same ArrayList instance and anyone who called getPeople() will now be able to change the actual instance anyway he/she likes, possibly corrupting what should be encapsulated state.

So you should return a copy of the ArrayList or wrap it with unmodifiable Decorator Collections.unmodifiableList(people)

Svilen
  • 1,377
  • 1
  • 16
  • 23