声明需要在子类中存在的字段的正确方法是什么?

3
假设我有一个类Animal,然后有一堆扩展了Animal的子类。假设我想要一个称为name的公共字段,在每个子类中也应该存在。在每个子类中包含和初始化这个字段的适当方法是什么?
1)将该字段声明为父级中的protected,然后在每个子类中初始化它。如果我用这种方法,是否可以将该字段称为super.variable或简单地称为variable?对我而言,使用super使得该字段在父级中被声明更加明显。(这是我目前正在做的)
2)将该字段声明为父级中的private,然后创建gettersetter来访问该字段
3)只需在每个子类中声明和初始化相同的变量
4)其他我可能忽略的方法?
感谢您的帮助。我明白这个问题相当基础,但我很好奇最正确的风格是什么。
编辑:
我不确定你们是否会看到这个问题,但这是一个跟进的问题。
有没有好的方法确保子类初始化该字段?

1
首选和标准的是选项2;这是最安全的方式,可以在setter中强制执行不变量以确保其维护。 - jason
1
你是否考虑过移除 name 字段,并将其作为一个抽象方法 name(),必须由子类重写? - arshajii
5个回答

4
答案取决于你是否需要控制对该字段的访问以确保正确性(例如,同时更新某个其他字段)。如果子类直接操作该字段可以被允许,那就只需使用protected。如果每次设置该字段时需要执行附加检查或操作,则应将其设置为超类的private,并使子类使用setter方法以确保运行您的逻辑。如果您知道它将始终需要,则不应复制该字段;如果不确定,则应考虑使用一个interface Animal并将该字段放在AbstractAnimal implements Animal中。
在Java中,除了调用超类版本的方法外,不使用super进行任何操作。直接访问protected字段;它们就是为此而存在的,如果需要了解它们的声明位置,则您的开发环境会跟踪它们。

我认为像 super.field 这样访问一个字段没有任何问题。这为其他开发人员提供了一些关于字段声明位置的上下文线索。 - Kevin Bowersox
在没有遮蔽标识符的情况下使用 super 只是多余的冗长。任何 IDE 都可以轻松告诉您该字段位于哪里。话虽如此,这是一个风格问题。 - chrylis -cautiouslyoptimistic-
我同意这是一个样式问题。虽然IDE可以告诉你字段的来源,但这需要开发人员进行一些工作。(Eclipse F3,ALT +右箭头)在Eclipse或其他IDE中是否有更快的方法? - Kevin Bowersox
在Eclipse中,只需将鼠标悬停在字段上,即可获得其声明路径、声明类型以及任何附加的Javadoc。 - chrylis -cautiouslyoptimistic-
我一直听说 super 只应该用于调用父类方法,但我真的看不出它背后的理由。我知道你可以使用 IDE 来获取这些信息,但哪种方式更容易:在变量列表中悬停每个变量以查看其声明位置,还是直接搜索 super。我每次都更倾向于后者。 - btse
如果你使用super关键字的存在来确定字段是在哪里声明的话,那么你就要依赖于其他人使用相同的非典型风格;你不能相信缺少super意味着它是在同一个类中声明的,所以你处于相同的位置,只是到处都有多余的文字。 - chrylis -cautiouslyoptimistic-

2

我投票给选项2:

创建一个私有字段,使用 setter 和 getter 方法(可以将它们设置为 protected,以使它们只能被子类访问)。

如果你不需要 setter(只需要 getter),其他选项包括:

4) 抽象 getter,并由子类决定如何实现

5) 私有 final 字段,由抽象类构造函数设置,还有一个 getter。


2

我总是将字段设为protected,因为这有助于调试和扩展,并在其上放置公共的getter和setter以创建“属性”。

(在各种开源库、Swing组件等中,私有字段一再成为我进行合法调试/扩展工程时的障碍。所以我相当反对它们。)

如果我担心可追溯性,在可能涉及行为或错误的情况下(例如获取并缓存值),我可能会通过getter在子类中访问变量。

我总是在写入变量时使用this.name--这有助于代码清晰度,并简化了setter中的参数命名。(对于参数和this.name字段,只需使用name即可。)

我不在读取变量时使用this--我想要清楚地了解写入操作。对于集合,我用List或map或其他方式给字段添加后缀,即childList——但是参数和局部变量是“children”。

我从不使用super来引用变量。Super仅在需要消除具有相同名称的继承和声明变量的歧义时才有意义,尽管可以合法地这样做——但几乎肯定会导致代码风格、清晰度和错误的问题。


我还喜欢使大多数属性可变——而不仅仅是在构造时设置。如果您想要使用Hibernate或持久化数据,这会有所帮助。过度依赖构造函数初始化往往会演变成困难——大而脆弱的调用签名,无法将类用于部分形成的数据或“特殊值”答案,以及初始化顺序问题。


1
有趣,我喜欢保护它以进行调试的想法。 - btse

2

我认为这取决于具体情况。如果名称字段应该是公开可访问的,我会将字段声明为私有的,然后创建公共的get/set方法。有时候你想要在派生类的公共接口中暴露基类上的字段。

如果名称字段只应在派生类内部使用,那么我会选择使用protected字段。

如果你想确保子类初始化一个字段,请在基类构造函数中添加一个参数,然后在基类中使用由派生类构造函数提供的参数初始化该字段。


如果你有get/set方法,为什么不把字段设为私有的呢? - Thilo
我有多少次想要调试或扩展Swing表格之类的东西,但却被人们将所有内容都设为私有而受挫。在进行合法工程方面,“私有”一直是我的一个重复性问题,我认为Java指南在这方面至少部分失败了。 - Thomas W
很少情况下非静态字段应该直接公开访问。 - arshajii
@btse 我添加了我的想法,关于在派生类中强制实现一个字段的问题。 - Kevin Bowersox

1
我通常使用选项2(私有+访问器-受保护的,不一定是公共的),以便有机会自定义变量访问。
关于您的编辑:如果构造函数名称是强制性要求,则强制使用。
Animal(String name) {
    this.name = name;
}

或者

String getName()  {
    if(null == name){
        name = initializeName();
    }
    return name;
}

并将initializeName()声明为抽象方法


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接