如何防止在同一个类中直接访问getterssetters之外的私有成员?

问题描述 投票:-1回答:1

挑战

给定一个具有非平凡的getter的类和其他一些访问相同的内部方法。private 成员字段。

有效实例

class TestType {
    private String value;

    public String getValue() {
        return this.value == null || this.value.isEmpty() ? "default" : this.value;
    }

    public void setValue(String newValue) {
        this.value = newValue;
    }

    public int getValueLength() {
        return this.getValue().length();
    }
}

我想禁止绕过getter在。getValueLength() 方法的以下实现 getValueLength() 应该产生某种错误(单元测试失败,检查样式错误,或者其他任何可以用通用方式自动化的错误)。

无效方法

    public int getValueLength() {
        // ERROR: possible NullPointerException
        return this.value.length();
    }

背景资料

在我的实战代码中,getter里面发生了一些懒惰加载的情况,只有在第一次访问时才会加载实际值。调用 getValueLength() 首先可能导致 NullPointerException 直接访问或根据缺失的值执行错误的操作。

其动机是为了确保未来的开发者不会忘记只使用getter方法,而从不直接访问成员。由于这是一个孤立的问题(即只有在这种懒加载被特别添加的情况下),如果需要一些额外的注释,例如在成员本身上,它应该只在选定的方法中被访问,并且这些方法也要被标记--或者特定的检查样式注释启用和禁用特定的规则,这也是可以接受的。

java getter-setter checkstyle
1个回答
2
投票

只是把这个答案用形式表达出来。

class LoadableVar<T> {

    private T val;
    private Supplier<T> loader;

    public LoadableVar(Supplier<? extends T> loader) {
        this.loader = loader;
    }

    public T get() {
        if (this.val == null) {
            //see: volatile and double-locking if multithreading
            this.val = this.loader.get();
        }
        return this.val;
    }

    public void set(T overwrite) { //WARN: ignores the loader!
        this.val = overwrite;
    }
}

然后,在把它应用到你的类中,

class TestType {
    private final LoadableVar<String> value;

    public TestType() {
        //can also be passed into the class, or done however you desire
        this.value = new LoadableVar<>(() -> /* load string from i/o, etc */);
    }

    public String getValue() {
        return this.value.get();
    }

    public void setValue(String newValue) {
        this.value.set(newValue); //I don't think this should be settable, personally
    }

    public int getValueLength() {
        return this.getValue().length();
    }
}

现在当你在TestType的范围内写代码时

String s;
s = this.value; //compile error!
s = this.getValue(); //OK
s = this.value.get(); //OK

正如你所看到的,在这样做的过程中,你也已经使... TestType#getValue 多余的,你可以简单地允许 value 拟做 protected 成员 (在这种情况下,我会删除这些设置器并使其不可变)。


1
投票

在代码中是没有办法强制执行的。

不过,由于你在评论中提到,"希望有办法通过某种工具来实现",而问题的标签是 我相信你可以创建一个自定义规则。

<module name="Regexp">
    <property name="id" value="valueField"/>
    <property name="format" value="\bvalue\b"/>
    <property name="illegalPattern" value="true"/>
    <property name="ignoreComments" value="true"/>
    <property name="message" value="Do not use 'value' field directly;  use getValue() instead."/>
</module>

然后,你会想抑制检查 你自己的 "有效 "行。

<module name="SuppressWithNearbyCommentFilter">
    <property name="idFormat" value="valueField"/>
</module>

而在代码中

private String value;   // SUPPRESS CHECKSTYLE

public String getValue() {
    return this.value == null || this.value.isEmpty() ? "default" : this.value; // SUPPRESS CHECKSTYLE
}

public void setValue(String newValue) {
    this.value = newValue;  // SUPPRESS CHECKSTYLE
}

(还有其他的方法可以用注释来抑制Checkstyle的检查 你可能会发现它更有视觉冲击力) 请看 过滤器 文档)。)

当实际运行Checkstyle时,你可能想把它限制在那一个源文件上。 例如,如果使用Ant,你可以这样做。

<taskdef resource="com/puppycrawl/tools/checkstyle/ant/checkstyle-ant-task.properties"
     classpath="tools/checkstyle-8.33-all.jar"/>

<checkstyle config="checkstyle.xml">
    <fileset dir="src/main/java" includes="**/TestType.java"/>
</checkstyle>

0
投票

没有办法保护一个成员不受它自己的类的影响, 但是你可以保护一个成员不受它的子类的影响. 例如,你可以这样做:没有办法保护一个成员不受自己类的影响,但是你可以保护一个成员不受它的子类的影响。

class TestParent {
    private String value;

    public String getValue() {
        return this.value == null || this.value.isEmpty() ? "default" : this.value;
    }

    public void setValue(String newValue) {
        this.value = newValue;
    }

}

class TestType extends TestParent {
    public int getValueLength() {
        return this.getValue().length();
    }
}

这样TestType仍然可以使用 setValue()getValue()但试图直接访问 this.value 不行,因为 value 可私自进入 TestParent

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