使用Java访问getter的安全性

时间:2015-05-24 10:38:57

标签: java getter-setter access-modifiers

因此,我们创建了一个带有一些私有类成员的简单类,并为它自动生成了getter。但是getter实际上返回了对该成员的引用,从而获得了对私有成员的完全访问权限。这样可以吗? 这是一个类的代码:

public class User {

    private ArrayList<String> strings = new ArrayList(){ {
            add("String1");
            add("String2");
        } };

    public User() {
    }

    public ArrayList<String> getStrings() {
        return strings;
    }

    public void setStrings(ArrayList<String> strings) {
        this.strings = strings;
    }
}

主要方法代码:

    public class Main {
    public static void main(String[] args){
        User user = new User();

        System.out.println(user.getStrings());
        user.getStrings().add("String3");
        System.out.println(user.getStrings());
    }
}

输出:

[String1,String2]

[String1,String2,String3]

我把吸气剂换成了这个:

public ArrayList<String> getStrings() {
    return (ArrayList<String>)strings.clone();
}

但问题仍然存在,即使不是为了安全,吸气剂是什么?什么是写它们的正确方法?

2 个答案:

答案 0 :(得分:2)

不,它不可行,因为它打破了封装,因此类不能维护自己的不变量。与构造函数相同。

但问题不在于getter / setter,而在于自动生成它们的代码。

简而言之:不要盲目使用自动生成的访问者,如果他们处理可变结构,则制作防御性副本(或不可变的等价物)。

顺便说一句,我不会有一个ArrayList返回类型的getter,即使它只是一个副本。它通常不是客户的业务,你返回的是什么样的列表,所以我的getter看起来像这样:

public List<String> getStrings() {
    return new ArrayList<>(strings);
}

或使用不可变视图:

public List<String> getStrings() {
    return Collections.unmodifiableList(strings);
}

或者使用Guava的ImmutableList课程:

public List<String> getStrings() {
    return ImmutableList.copyOf(strings);
}

三种解决方案之间存在细微差别,因此最佳解决方案可能会有所不同。作为一般规则,我更喜欢返回不可变结构,因为这清楚地表明对结构所做的更改不会被反映出来,即user.getStrings().add( "X" );将因异常而失败。

您向我们展示的代码的另一个微妙问题是双括号初始化。想象一下这样的课程:

public class Foo {
   private List<String> strings = new ArrayList() {{ add("bar");}};
   private Object veryLargeField; //the object stored here consumes a lot of memory

   public List<String> getStrings() {
     return strings;
   }
}

现在想象一下我们这样做:

private class Bar {
   private List<String> fooStrings;

   public Bar() {
     this.fooStrings = new Foo().getStrings();
   }
}

Bar消耗多少内存(或使用精确术语:保留)?好吧,结果很多,因为你用双括号初始化做的是创建一个匿名内部类,它将包含对其外部类(Foo)的引用,因此返回的列表是可访问的Foo的所有其他字段都不符合垃圾回收的条件。

答案 1 :(得分:0)

从我的观点来看,吸气剂通常应该有两个目的:

  • 首先他们应该保护实施细节。
  • 第二,他们应该提供一种容易扩展的方法(例如验证或仪器)

如果您的示例违反了这些原则,则取决于具体情况:

  1. 如果您的类应该拥有字符串,那么可能每个人都应该与容器对象进行交互以修改列表而不是列表本身。要公开集合(例如,对于需要集合的方法中的处理),您可以使用例如Collections.unmodifiableList()。另一方面,如果类只拥有字符串列表,那么它不是具有列表的实现细节。
  2. 使用getter而不是直接访问字段,可以轻松添加数据对话,跟踪工具和其他内容,而无需更改使用该字段的所有位置。