如何确保一个字段被正确封装?

maa*_*nus 6 java encapsulation static-analysis

我想知道是否有一种简单的方法可以找到直接访问字段的所有方法。更确切地说:

我想确保只有一种方法可以写入一个字段,并且只有一种方法可以读取它。所有其他访问都应使用这两个。

背景:当一个字段被写入时,我需要在某个地方记录这个事实,我可以使用生成的 setter 轻松地做到这一点,但我想确保我不会在某处绕过它。

它用于移动设备而不是服务器,所以我不想/不能使用接口或运行时字节码重写...

我知道,有 ASM,但 AFAIK 使用它意味着我想花更多的工作。我希望,有更好的方法。

更新

没想到,还得声明允许修改代码,但是内存比较紧。因此封装字段(例如,Java FX 样式)或进行备份太糟糕了。有很多领域,所以实际上任何需要接触它们的东西都不好。

我可以想象解析来源,这要么很复杂,要么容易出现误报,因为相同的标识符根据上下文具有不同的含义。它甚至可能被隐藏(例如,在声明同名变量的嵌套类中),但我很乐意更改代码以避免出现问题。

从类文件中获取结构化信息肯定会更好。

kay*_*ya3 6

Java 中的封装是在对象或类级别;最严格的访问控制修饰符是private,但即便如此,同一类中的每个方法都可以访问private字段。因此,如果要封装字段的行为,可以通过将字段表示为对象来实现。

这是一个表示可变字段的类:

public class MyField<T> {
    private T value;

    MyField(T initialValue) {
        value = initialValue;
    }

    public T get() {
        return value;
    }

    public void set(T newValue) {
        // any logging goes here
        value = newValue;
    }
}
Run Code Online (Sandbox Code Playgroud)

然后,如果您希望将此日志记录行为应用于字段name,请声明name为 typeMyField<String>而不是String

public class Person {
    private final MyField<String> name;
    private final MyField<Integer> age;

    public Person(String name, int age) {
        this.name = new MyField<>(name);
        this.age = new MyField<>(age);
    }

    public String getName() {
        return name.get();
    }

    public int getAge() {
        return age.get();
    }

    public void setName(String name) {
        this.name.set(name);
    }

    public void setAge(int age) {
        this.age.set(age);
    }
}
Run Code Online (Sandbox Code Playgroud)

好处:

  • 因为MyField.value是私有的,所以很容易在所有实例中验证它的值在没有触发日志记录行为的情况下永远不会设置。
  • Java 编译器的静态检查足以检查字段的值是否只能通过其getset方法访问;不需要单独的验证阶段。
  • 您可以仅将其应用于您想要记录行为的字段。

缺点:

  • 这些MyField对象中的每一个都有一些内存使用开销。
  • 额外的方法调用getset会在运行时产生一些开销。
  • 要调用的额外代码.get()并不多,但它可能会损害更复杂表达式的可读性。