tags:

views:

293

answers:

6

In a software development class at my university, the teacher kept mentioning that on a quiz we needed to make sure that a field returned by a getter needed to be "protected." I guess she meant that nothing outside the class should be able to change it. She didn't give much more of an explanation than that.

For instance:

class Foo {
    string[] bar = <some array contents>;

    public string[] getBar() {
        return bar;
    }
}

Any code calling getBar would be able to modify the elements in that array. How do you prevent that from happening? I'm assuming that the object itself should be able to modify the array, just not anything outside the object.

This isn't homework help since the quiz is a couple of weeks old. I simply want to understand Java better since my teacher didn't explain very well.

Update: The teacher wouldn't merely allow us to use protected as the access modifier on the field.

+8  A: 

You either use a collection and wrap it in Collections.unmodifiable*() or you defensively copy your array, collection or object if its mutable (which arrays always are).

For example:

class Foo {
    private String[] bar = <some array contents>;

    public String[] getBar() {
        return bar == null ? bar : Arrays.copyOf(bar);
    }
}

What you have to watch out for is that this is a shallow copy (so is clone). Not sure what your teacher's problem with clone was.

cletus
Would bar.clone() work? I tried that on the quiz, but she said it didn't work, even though it seemed to work in my own code.
Bob
bar.clone() creates shallow copy of array ... i.e. new array will reference to same elements as original array.
Peter Štibraný
@Peter yeah but so does System.arraycopy() (and Arrays.copyOf() which is just a wrapper for that).
cletus
Maybe the explanation is that the teacher was unsure about this, too. Just being a teacher doesn't mean she can know everything. Maybe she meant that the field must be private and didn't care that you could modify the elements of the array.
Aaron Digulla
I guess the teacher wanted a deep copy. What's the best way to make a deep copy in Java?
Bob
There isn't really a good way.
Michael Borgwardt
+3  A: 

I suspect what she meant was that the visibility of the field itself should be protected (or private) so that access only occurs through the getter. In the case of a collection, you may also want to do as @cletus suggests and return a copy of the collection if you don't want it to be modified outside the class. EDIT Based on your edit, she probably meant both.

class Foo {
    protected string[] bar = <some array contents>;

    public string[] getBar() {
        return bar;
    }
}
tvanfosson
Yep - was just saying something like this. Also, for more info, there's some at http://mindprod.com/jgloss/protectedscope.html
Stobor
+1  A: 

To protect that field from being changed you need to first make it private and don't provide any setter of any other method which changes that field. This way nobody can change the reference of that variable.

If the field is a mutable Object then again its value can be changed. For that you would need to do deep cloning before returning that object.

Bhushan
+5  A: 

Just to add to one of the previous answers, you want to make sure that with a collection you aren't using the clone() method to achieve what you are trying to achieve here. This creates a shallow copy of the collection only, all object references contained in the collection copy still point to the same objects as in the original, e.g. the objects in the collection copy can still be modified, even though the original collection cannot. Be sure you are making a deep copy of a returned collection if this is what you are trying to do.

Mark
+1  A: 

I'd add to cletus' first suggestion - the easiest way of making bar immutable would be to use a List instead of an array and return it wrapped in an unmodifiableList. That way it's immediately clear to the client of the class that the contents of bar can't be altered - an UnsupportedOperationException is thrown. Messing about with deep cloning will probably be pretty inefficient, depending on the complexity of your objects, and still returns a bunch of mutable objects - it's just that any changes made to those will be ignored by Foo.

class Foo {
  private List<String> bar = new ArrayList<String>();
  public Collection<String> getBar() {
    return Collection.unmodifiableList(bar);
  }
}

(Also might be worth noting that with generics in Java 5+, a list behaves much more like an array than it used to).

Martin Dow
+1  A: 

Please tell the professor that all non-final fields must be private to preserve encapsulation.

Protected allows your subclass or other classes in the same package to modify the field without your class knowing it.

The only class that should touch non-final fields directly is the class that defines them.

(Think about what would happen if you wanted to later fire an event when the field changes... you can only do that if all access is through the setter...)

Scott Stanchfield