views:

330

answers:

5

Hi, i'm starting with Java and i'm learning about setters,getters and encapsulation.

I have a very simple program, 2 classes:

  • Container has a private int array (numArray) with his setter & getter.

  • Main creates a Container object and uses it in totalArray method.


public class Container {
    private int numArray[]= {0,0,0};
    public int[] getNumArray() {
        return numArray;
    }
    public void setNumArray(int index, int value){
        numArray[index] = value;
    }    
}

public class Main {
    public static void main(String[] args) {
        Container conte = new Container();
        System.out.println(totalArray(conte.getNumArray()));
        conte.getNumArray()[2]++;
        System.out.println(totalArray(conte.getNumArray()));
    }
    private static int totalArray (int v[]){
        int total=0;
        for (int conta =0; conta<v.length;conta++){
            total+=v[conta];
        }
        return total;
    }
}

Problem: I can change the private int array through the getter, i know that's because getNumArray returns a reference to numArray not the array itself. If I were interested in a single element of the array, i'd make a getter with an index value, but i want the hole array for the totalArray method.

How can i prevent numArray from being modified out of his class?

Sorry for my poor English ^^. Thx

+2  A: 

Typically you would look at the interface you're trying to provide to callers of your class.

Methods like:

void addItem(Object item);
Object getItem(int index);
int getSize();

are the sort of things you would provide in your container class. They then interact with the private array on the caller's behalf.

If you want to return the whole array without allowing changes you could in the getter copy the array into a new one and return the copy.

Alternatively, if you use the Java collection classes instead of the primitive array they provide an unmodifiableXXX() method (e.g. Collections.unmodifiableList(myList)) which provide a read-only wrapper around the collection.

Paolo
Please fix your method names. Java methods start with lower case letters and Object is a class (upper case).
Aaron Digulla
Thanks, this is what happens when you spend a day looking at C#...;)
Paolo
+1: Encapsulate your array by **not exposing the existence of the array at all**! Make the container have a rich interface that represents actual functions on that class, rather than just returning its internal data members to callers to do with as they wish.
Andrzej Doyle
+4  A: 

All you can do to prevent people from changing your array is to provide a copy of it in the getter.

public int[] getArray() {
    return Arrays.copyOf(numArray, numArray.length);
}

This way, other methods can change their own copy of the array, but when they call the getter again, they get the original version, unchanged. Only the setNumArray() you provide can actually modify your internal array.

Otherwise, if you want to completely block the container, you have to drop arrays and use an immutable object. Some libraries provide immutable lists, or use Collections.unmodifiableList.

subtenante
'Some' as in the standard Java library.
Pete Kirkham
@Pete : yes but not only.
subtenante
+2  A: 

If you want to return an array, you would clone it:

  public int[] getArray() {
       return (int[]) numArray.clone();
  }

In a public API you should be sure to document that clearly to the callers (really either way, if they are getting an array that will change the state of the class or not - they need to know).

Yishai
A: 

The issue does get complicated when u had non primitive array types as you may want to clone even the objects stored in the array to ensure they are not modified by API callers. Any good way of handling this?

Fazal
A: 

Encapsulation is the process of hiding the implementation. Whether the collection stores its data in an array or not is an implementation detail; if it was encapsulated you would want to be able to change it to another storage type.

The very fact that you are exposing state ( or derived state ) as getters and setters breaks encapsulation and implies you are implementing an abstract data type rather than a true object-oriented class. For example, an ArrayList is a data type which does not represent any true encapsulation of behaviour in an application. Whether this is what you want depends how and where the type is to be used.

I would tend to either make Container implement Iterable<Integer> for external interation if it is simply a container data type, or provide an internal iterator method to which you pass a visitor if it intended as an encapsulated class. If it is an abstract data type, strongly consider using the built-in ones such as int[] or List<Integer> instead.

Pete Kirkham