Getters and setters for arrays

2020-03-01 08:57发布

问题:

I have a few questions about getters and setters for arrays. Suppose we have a class like this, which makes a private copy of an array in its constructor:

import java.util.Arrays;

public class Foo
{
    private int[] array;

    public Foo(int[] array) {
        this.array = Arrays.copyOf(array, array.length);
    }
}

We want the array to only be accessed/mutated via the getters and setters. If we have a getter that looks like this:

public int[] getArray() {
    return array;
}

it defeats the purpose of the getter as we're returning a reference that allows the user to directly modify the elements of the array. e.g.

Foo foo = new Foo(someArray);
...
int[] bar = foo.getArray();
bar[0] = 123; // Now foo.array[0] = 123 and we haven't used a setter!

So presumably we need something like this:

public int getElement(int index) {
    return array[index];
}

Similarly for setters. But if we're doing things on a per-element basis, we're also going to need to provide a means of getting the length:

public int getArrayLength() {
    return array.length;
}

This is already a little messy for a 1-dimensional array, but say we have a multidimensional array instead:

import java.util.Arrays;

public class Foo
{
    private int[][][] array;

    public Foo(int[][][] array) {
        // Code for making a deep copy here
    }

    public int getElement(int i, int j, int k) {
        return array[i][j][k];
    }

    public void setElement(int i, int j, int k, int value) {
        array[i][j][k] = value;
    }

    public int getArrayLength() {
        return array.length;
    }

    public int getArrayLength(int i) {
        return array[i].length;
    }

    public int getArrayLength(int i, int j) {
        return array[i][j].length;
    }
}

This is a lot of code for such a trivial task, and more importantly it's a mess to actually use. Do we really have to end up with something like this, or is there a better way to do it? I've looked all over the place and there doesn't seem to be a "standard practice" for this.

回答1:

A multi-dimensional array is also a 1-dimensional array: int[a][b][c] is really just int[a*b*c], so the problem boils down to, how do you provide access safely? Simply like this:

public class Foo {
    private int[] array;

    public Foo(int[] array) {
        this.array = Arrays.copyOf(array, array.length);
    }

    /** @return a copy of the array */
    public int[] getArray() {
        return Arrays.copyOf(array, array.length);
    }
}

That's it.

Callers have a safe copy of the array and can use it in the full normal way arrays are used. No need for delegator methods.



回答2:

What do you think ArrayList does, or Vector before it?

I think the better question is why, at this point, are you exposing that Foo is backed by an array at all? If you're trying to encapsulate it, you don't need to have accessors and setters all over the place. If you're just trying to create a class wrapper around the array, then I would suggest you have an interface, call it IntList or something, and make Foo a concrete implementation that's backed by the list.



回答3:

In relation to the first part could your getter not look like the constructor?

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


回答4:

I wrote a small API for multidimensional generic arrays. There you have getters, setters for each element, whatever your dimensions are.

MDDAJ on github

Here is a example: creating a 5 dimensional string array:

MDDAPseudo<String> c = new MDDAPseudo<String>(10,20,5,8,15);
c.set("xyz", 0,0,2,1,0); // setter indices: [0][0][2][1][0]
String s = c.get(0,0,0,0,0); // getter indices [0][0][0][0][0]

Bohemian already wrote you can use only one dimension. In this case the class PDDAPSeudo internal have one dimension, too. But the API provides you access like a multi dimensional array