如何在Java中封装数组

问题描述 投票:8回答:8

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

  • Container有一个私人int数组(numArray)与他的二传手和吸气剂。
  • 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被修改出他的班级?

java arrays encapsulation getter
8个回答
8
投票

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

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

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

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


6
投票

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

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

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


3
投票

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

方法如:

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

是你在容器类中提供的东西。然后,他们代表呼叫者与私有阵列进行交互。

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

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


0
投票

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

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

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


0
投票

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

private A[] items;

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

0
投票

如何在Java中封装数组

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

如果你坚持然后返回一个克隆数组/防御副本是简单的情况下的方法。


0
投票

我想从我在这里看到的所有答案中提出一个不同的方法。当您考虑封装时,它也很方便,也可以考虑规则“不要向对象询问其数据,请求对象为您的数据操作”。

你没有给我使用numArray,我打算假装你的目标是从数学(不是Java Vector)创建一个数字“Vector”,例如用途。

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

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

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

这个(和OO一般)经常失败的情况是库 - 当你真的想要为你的数组添加一些功能但仍然把它作为通用数组时。在这种情况下,Java通常根本不会使用封装,它只是添加了一个可以对集合/数组执行操作的辅助方法/类(查看“Arrays”对象以获得一些很好的示例)。


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的后备数组中设置正确的项目数,并减少创建的中间对象的数量。

© www.soinside.com 2019 - 2024. All rights reserved.