-1

I'm new to Java and I wonder why it is possible to set the value of a private attribute this way, without using a setter ? :

class Example {
   private int[] thing;
   public void initialize() {
       thing = new int[10];
   }
   public int[] getThing() { 
       return thing;
   }
}
class myclass {
   public static void main(String args[]) {
       Example e = new Example();
       e.initialize();
       e.getThing()[0] = 1;
       System.out.println(e.getThing()[0]) // value is still 1...
}

I really do not understand why this is legal...

Edit: I expected e.getThing() to return the value of thing not a reference to thing and even if it was the case, as "thing" is private I thought I wont be able to modify it directly.

erebos007
  • 31
  • 5
  • Are you sure that you are setting the value to the **private member of other class** ? – Mohammed Atif Dec 27 '16 at 14:57
  • 5
    What are you expecting ? – AxelH Dec 27 '16 at 14:58
  • If you want to make thing read only I would do this in one of two ways. 1) make a defensive copy of the array, where I create another array with `new` and return this one. 2) Uses a List (eg. ArrayList) instead and return an `Iterable` (which is an interface implemented by all lists). The only thing that can be done with an `Iterable` is iterating more or less, (though it is possible to modify the objects in the iterable if they are aggregates). – patrik Dec 27 '16 at 15:05
  • @patrik thx. I wanted 'thing' not to be modified without a setter. – erebos007 Dec 27 '16 at 15:25

4 Answers4

8

You're exposing the reference to an array from the getter, and the array contents themselves are mutable i.e. you can change them. So in your example above you return a reference to the array held by the private variable in the class, you amend that array, and that change will be visible via the getter going forwards because it always returns that modified array.

To avoid this, what you could do is either:

  1. return a read-only view of the collection via your getter. This could be fast, but if the parent object changes the array contents under you whilst you're (say) iterating over it, then this could be confusing
  2. return a defensive copy via your getter. This would mean returning a new array copy per call, and this wouldn't change under you, but you pay a cost in copying it for each call

In your specific example above, you could create a read-only collection within your initialize() method, and then you're not going to have this issue.

I think you need to be clear re. private (i.e. the reference variable is not available outside the class) and mutable (the object you're returning can change). These are two very different concepts.

Brian Agnew
  • 268,207
  • 37
  • 334
  • 440
  • thx. I thought e.getThing() would return the value of thing not its reference in memory allowing it to be modified directly, eventually bypassing any setter. – erebos007 Dec 27 '16 at 15:16
2
private int[] thing;

says that the reference to thing is private and therefor cannot be accessed from outside the class. Fortunately, you have a getter so you get the reference.

As arrays are mutable, you can modify the content of the array.

What you can't do is to set new thing:

e.thing = new int[5];

and this is what private does.

xenteros
  • 15,586
  • 12
  • 56
  • 91
  • You haven't explained why `initialize` is able to assign the variable a value even when called from another class. That's the question. – nvioli Dec 27 '16 at 14:59
  • I'm not it is, is it ? – Brian Agnew Dec 27 '16 at 15:01
  • @BrianAgnew what do you mean? – xenteros Dec 27 '16 at 15:01
  • 4
    @nvioli that´s not the question, the question is why the value `1` is present at index `0` when he did let `getThing` return the reference to the array, as OP probably thinks it´s returning a copy of the array due to it beeing `private` or something like that (it´s actually unknown why and what he thinks is happening, but that´s my interpretation of his comment inside the code snippet). – SomeJavaGuy Dec 27 '16 at 15:02
  • 1
    @xenteros - I'm replying to nvioli – Brian Agnew Dec 27 '16 at 15:03
0

This is because private members are directly accessible only in class they are defined, you can not access them outside of the class unless you provide some public accessor method. You are providing a public accessor to an array reference and after getting the reference you are changing the values

hhafeez
  • 73
  • 9
-2

The fact that thing is private means you can't

Example e = new Example();
e.thing = new int[10];

However, you've chosen to expose initialize publicly, so you can call it from another class. The implementation details of initialize are knowable only to the author of the Example class. That author may do whatever he or she wants within that class, including doing anything he or she wants with private fields.

nvioli
  • 4,137
  • 3
  • 22
  • 38
  • Still you did not anser the question. – lexicore Dec 27 '16 at 15:15
  • I thought the confusion was about the ability to set a `private` field from outside the class so that's what I answered; maybe I misunderstood what OP was confused about. – nvioli Dec 27 '16 at 15:16
  • I believe so. The question is why you can change the values of the array *despite* the field being `private`. – lexicore Dec 27 '16 at 15:17
  • Sorry I do not understand. If I delete the initialize() method then declare thing with private int[] thing = new int[10] – erebos007 Dec 27 '16 at 15:21
  • @erebos007 assuming you're talking about doing it from another class, that _will not work_ because `thing` is private, meaning no other class has access to it. However, `initialize` is local to the `Example` class, so it has access to `thing` despite `thing` being private. – nvioli Dec 27 '16 at 15:31