Question

I'm starting with Java and I'm learning about setters, getters and encapsulation. I have a very simple program, two 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 whole array for the totalArray method.

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

Was it helpful?

Solution

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.

OTHER TIPS

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).

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.

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.

There is yet another way, which does not make a copy of the array, but has other drawbacks. I would use it for very large arrays:

private A[] items;

public List<A> getItems() {
    return Collections.unmodifiableList(Arrays.asList(items));
}

How to encapsulate an array in Java

The question might be clear enough, however I guess the point of OOP is to design a class as an entity that manipulates this array and extracts it's own api.

If you persist then return a clone of the array / defensive copy is the way to go for simple cases.

I would like to suggest a different approach from all the answers I saw here. It's handy when you are thinking of encapsulation to also think of the rule "Don't ask an object for its data, ask an object to operate on its data for you".

You didn't give me a use for numArray, I'm going to pretend your target was to create a numeric "Vector" from math (not a Java Vector) for example purposes.

So you create a NumericVector class that contains an array of doubles. Your NumericVector would have methods like multiplyByScalar(double scalar) and addVector(NumericVector secondVector) to add elements.

Your internal array is completely encapsulated - it never escapes. Any operation done on it is done in your NumericVector class via these "business methods". How do you display it after operating on it? Have NumericVector override NumericVector.toString() so it prints out correctly, or if you have a GUI, write a "Controller" class to transfer data from your Model (NumbericVector) to your view (GUI). This might require a way to stream elements from your NumericVector.

This also indicates a few things to avoid: Don't automatically create setters and getters, they break your encapsulation. You often need getters but, as others here have said, you should make your getter return an immutable version of the array. Also try to make your classes immutable wherever possible. That method I mentioned earlier numericVector.addVector(NumericVector secondVector) should probably not modify numericVector but return a new NumericVector with the solution.

The case where this (and OO in general) often fail is libraries - when you really want to add a little functionality to your array but are still leaving it as a general purpose array. In this case Java doesn't usually bother with encapsulation at all, it just adds a helper method/class that can do things to the collection/array (look at the "Arrays" object for a bunch of great examples).

A memory efficient way to do this is...

package com.eric.stackoverflow.example;

import java.util.List;

import com.google.common.collect.ImmutableList;

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

This allows the ImmutableList to set the correct number of items in the backing array of the List and reduces the number of intermediate objects that are created.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top