【问题标题】:How to encapsulate an array in Java如何在Java中封装一个数组
【发布时间】:2010-01-05 15:04:31
【问题描述】:

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

  • Container 有一个私有 int 数组 (numArray) 和他的 setter 和 getter。

  • 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 更改私有 int 数组,我知道这是因为 getNumArray 返回对 numArray 的引用,而不是数组本身。如果我对数组的单个元素感兴趣,我会创建一个带有索引值的 getter,但我想要 totalArray 方法的整个数组。

如何防止numArray 被修改出他的班级?

【问题讨论】:

    标签: java arrays encapsulation getter


    【解决方案1】:

    为了防止人们更改您的数组,您所能做的就是在 getter 中提供它的副本。

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

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

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

    【讨论】:

    • 'Some' 与标准 Java 库中的一样。
    【解决方案2】:

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

      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)),该方法为集合提供只读包装。

      【讨论】:

      • 请修正您的方法名称。 Java 方法以小写字母开头,Object 是一个类(大写)。
      • 谢谢,这就是你花一天时间看 C# 时会发生的事情......;)
      • +1:通过完全不暴露数组的存在来封装您的数组!让容器有一个丰富的接口来表示该类上的实际功能,而不是仅仅将其内部数据成员返回给调用者以随心所欲地处理。
      【解决方案4】:

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

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

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

      【讨论】:

        【解决方案5】:

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

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

        【讨论】:

          【解决方案6】:

          如何在Java中封装数组

          这个问题可能已经很清楚了,但是我猜 OOP 的重点是设计一个一个作为实体的类,它可以操纵这个数组并提取它自己的 api。

          如果您坚持,则返回 数组克隆 / 防御性副本简单情况下的方法强>。

          【讨论】:

            【解决方案7】:

            我想提出一种与我在这里看到的所有答案不同的方法。当您考虑封装时,还可以考虑“不要向对象询问其数据,而是要求对象为您操作其数据”这一规则。

            你没有给我numArray 的用途,我假设你的目标是从数学(不是 Java 向量)中创建一个数字“向量”,例如。

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

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

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

            这种(以及一般的 OO)经常失败的情况是库 - 当您真的想向您的数组添加一些功能但仍将其保留为通用数组时。在这种情况下,Java 通常根本不关心封装,它只是添加了一个辅助方法/类,可以对集合/数组做一些事情(查看“Arrays”对象以获得一堆很好的示例)。

            【讨论】:

              【解决方案8】:

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

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

              【讨论】:

                猜你喜欢
                • 2018-07-26
                • 1970-01-01
                • 1970-01-01
                • 2012-05-06
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 1970-01-01
                • 2016-11-03
                相关资源
                最近更新 更多