如何在Java中封装数组

时间:2010-01-05 15:04:32

标签: java arrays encapsulation getter

我从Java开始,我正在学习setter,getters和encapsulation。我有一个非常简单的程序,两个类:

  • Container有一个私有的int数组(numArray)和他的setter&吸气剂。

  • Main创建了一个Container对象,并在totalArray方法中使用它。


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;
    }
}

问题:我可以通过getter更改private int数组,我知道这是因为getNumArray返回对numArray的引用,而不是数组本身。如果我对数组的单个元素感兴趣,我会使用索引值创建一个getter,但我希望整个数组用于totalArray方法。

如何阻止numArray被修改出来?

8 个答案:

答案 0 :(得分:8)

你可以做的就是阻止人们更改你的阵列,就是在getter中提供它的副本。

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

这样,其他方法可以更改自己的数组副本,但是当它们再次调用getter时,它们会获得原始版本,不变。只有您提供的setNumArray()才能实际修改内部数组。

否则,如果要完全阻止容器,则必须删除数组并使用不可变对象。 Some库提供不可变列表,或使用Collections.unmodifiableList

答案 1 :(得分:6)

如果你想返回一个数组,你可以克隆它:

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

在公共API中,您应该确保向调用者清楚地记录(实际上,无论哪种方式,如果他们获得的数组将改变类的状态 - 他们需要知道)。

答案 2 :(得分:3)

通常,您会查看您尝试向班级来电者提供的界面。

方法如:

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

是你在容器类中提供的东西。然后,他们代表调用者与私有数组进行交互。

如果要返回整个数组而不允许更改,可以在getter中将数组复制到新数组中并返回副本。

或者,如果您使用Java集合类而不是原始数组,则它们提供了一个不可修改的XXX()方法(例如Collections.unmodifiableList(myList)),它为集合提供了一个只读包装。

答案 3 :(得分:0)

封装是隐藏实现的过程。集合是否将数据存储在数组中是一个实现细节;如果它是封装的,你可能希望能够将其更改为另一种存储类型。

事实上,您将状态(或派生状态)公开为getter和setter会破坏封装,并暗示您正在实现抽象数据类型而不是真正的面向对象类。例如,ArrayList是一种数据类型,它不代表应用程序中任何行为的真正封装。这是否是您想要的取决于使用类型的方式和位置。

如果它只是一个容器数据类型,我倾向于让Container实现Iterable<Integer>用于外部交互,或者提供一个内部迭代器方法,如果它打算作为封装,你可以将访问者传递给它类。如果它是抽象数据类型,请强烈考虑使用内置类型,例如int[]List<Integer>

答案 4 :(得分:0)

还有另一种方法,它不会复制数组,但还有其他缺点。我会将它用于非常大的数组:

private A[] items;

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

答案 5 :(得分:0)

我想从我在这里看到的所有答案中提出一个不同的方法。当你考虑封装时也要方便考虑规则&#34;不要向对象询问数据,要求对象为你操作数据&#34;。

你没有给我使用numArray,我假装你的目标是创建一个数字&#34; Vector&#34;例如,来自math(不是Java Vector)。

因此,您创建一个包含双精度数组的NumericVector类。您的NumericVector将使用multiplyByScalar(double scalar)和addVector(NumericVector secondVector)等方法来添加元素。

您的内部数组已完全封装 - 它永远不会逃脱。对它进行的任何操作都是通过这些&#34;业务方法&#34;在NumericVector类中完成的。操作后如何显示?让NumericVector覆盖NumericVector.toString()以便正确打印出来,或者如果你有一个GUI,写一个&#34; Controller&#34;用于将数据从Model(NumbericVector)传输到视图(GUI)的类。这可能需要一种方法来流式传输NumericVector中的元素。

这也表明要避免的一些事情:不要自动创建setter和getter,它们会打破你的封装。你经常需要getter,但正如其他人所说,你应该让你的getter返回一个不可变的数组版本。也尽量让你的类不可变。我之前提到的那个方法numericVector.addVector(NumericVector secondVector)可能不应该修改numericVector,而是返回一个新的NumericVector和解决方案。

这个(通常是OO)经常失败的情况是库 - 当你真的想要为你的数组添加一些功能但仍然把它作为通用数组时。在这种情况下,Java通常不会对封装感到烦恼,它只是添加了一个可以对集合/数组做事的辅助方法/类(查看&#34; Arrays&#34;对象为一堆很好的例子)。

答案 6 :(得分:0)

  

如何在Java中封装数组

问题可能很清楚,但我想 OOP 的意思是设计一个类作为实体来操纵这个数组并提取它自己的api。< / p>

如果你坚持下去,那么返回数组 / 防御性副本的克隆 的简单案例强>

答案 7 :(得分:0)

执行此操作的内存有效方法是......

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;
    }    
}

这允许ImmutableListList的后备数组中设置正确的项目数,并减少创建的中间对象的数量。