【问题标题】:Java protected fields vs public gettersJava 受保护字段与公共 getter
【发布时间】:2011-01-17 18:58:04
【问题描述】:

什么是更好的做法和原因:通过受保护字段或私有字段上的公共 getter 访问基类变量。

(无论如何,getter 都会公开)

【问题讨论】:

标签: java


【解决方案1】:

如果无论如何都会有一个公共吸气剂,为什么要比绝对必要的更广泛地公开该领域本身?这意味着它可以立即被子类写入(除非它是 final 开始的)。

我个人喜欢我的所有字段都是私有的:它在 API 和实现之间提供了更清晰的分离。我认为超类和子类之间的关系类似于调用者和被调用者的关系——对底层实现的更改不应该破坏子类,就像它们不应该破坏调用者一样。字段的名称是一个实现细节,不应影响其他类。

诚然,我的观点有时被视为有些极端......

【讨论】:

  • 在我公认的有限经验中,我还没有找到使用受保护字段的充分理由,而且似乎总是会导致其他问题。我同意在几乎所有情况下都最好将它们设为私有。
  • @Jon。您能否为这种情况提供一个工作示例/说明,其中超类中的变量/方法都在子类中使用。如果您能回复,非常感谢您
  • @Deepak:恐怕我没有时间为我给出的每个 Java 答案给出一个完整的例子。何不自己尝试一下,遇到问题就问一个问题?
  • 想改写此答案中的第二段。使用function 或class 设计一个抽象 实际上会驱使您选择相应的访问说明符。 SICP 定义说:抽象在实现细节和使用之间造成了障碍。
  • @overexchange:我想我宁愿把它作为你的陈述留在评论中,而不是用它来编辑答案。
【解决方案2】:

您应该始终针对类的公共 API 进行编程,即使用公共方法。

原因很简单。在未来的某一天,您或其他人可能想要更改实现。这应该总是可行的。如果你依赖实例变量,你就会限制自己。

此外,当访问变量时,您无法控制该变量是否为只读,也无法在该变量更改时添加检查。

如果你使用 setter/getter,你总是可以在以后添加验证、检查等。您也可以只提供一个 getter 来使变量只读。

【讨论】:

  • 如果有人暴露一个变量,他们应该像对待公共/受保护的方法一样将其视为不可更改。如果有人公开一个字段然后更改它,他们显然不关心首先破坏其他开发人员,并且很可能也会更改方法:(
  • 是的。这就是为什么我在我的评论中添加了这么多“应该”的原因 :) 但大多数时候维护现有的公共方法更容易,只需添加一个重载/特殊版本,这就是为什么我更喜欢 privat 变量(公共静态除外最终常数)
【解决方案3】:

直接字段访问不是首选。使用 public 或 protected setter 和 getter。

getter 不必是public - 如果您想对“外人”隐藏数据,但将数据提供给子类,请使用protected

【讨论】:

    【解决方案4】:

    Sun 关于控制对字段的访问的一些建议是here. 请注意,使字段受保护也会将其暴露给包,而不仅仅是子类。通常,如上面链接所述,除非有充分的理由不这样做,否则字段应该是私有的。

    【讨论】:

    • +1 提到 protected 暗示 package private 访问级别
    【解决方案5】:

    Effective Java 2nd Edition 说

    第 13 条:最小化类和成员的可访问性

    经验法则很简单:让每个类或成员都像 可能的。换句话说,使用与 您正在编写的软件的正常运行。

    因此,如果您不确定为什么需要受保护的类成员(即,您不需要同一包中的子类或类可以访问该字段),则将其声明为私有。如果你想在课堂外设置它,那么就创建一个公共设置器。

    但是,如果您的成员是最终成员,那么在某些情况下对其进行保护可能是可以的(即它不会泄露敏感信息)。

    我想提一下的一个潜在的安全问题是,如果您有一个声明为 protected final 的数组(甚至是 public final),则数组引用是 final 的(不能修改),但数组中保存的对象不是 final 的(入侵者可以更改数组内容)。


    如果你懂 c++,你可能知道

    const int * someMember

    不同于

    int * const someMember

    后者就像java中的final数组。


    上述安全漏洞的修复方法是返回数组的深层副本或将其作为只读列表返回。

    【讨论】:

      【解决方案6】:

      通常,您应该使用 Sun 的建议。有一个很大的例外:如果您正在为 Android 编程。

      原因是性能。对于每个虚拟方法调用,使用查找表将方法路由到其对象都会产生开销。访问局部变量时不涉及此开销。

      这里有一些链接可以更深入地解释这一点:

      http://developer.android.com/training/articles/perf-tips.html#GettersSetters

      http://blog.leocad.io/why-you-shouldnt-use-getters-and-setters-on-android/

      了解您要完成的工作很重要:

      1. 客户端代码应该可以使用公共接口访问该字段的值。
      2. 该字段旨在供子类使用。

      在普通的 Java 中,getter 和 setter 完成这两项任务。但安卓不一样。如果你在做#1,那么你应该使用公共的 getter 和 setter。如果你在做#2,那么你应该使用受保护的字段。如果您同时使用两者,请同时使用。

      【讨论】:

        【解决方案7】:

        我想向您介绍一些保护 Java 中“受保护”字段的参数: “在需要避免值验证的情况下,您可能更喜欢使用受保护的字段而不是公共访问器来访问基类成员”。 但是,如果不是这种情况,则应使用具有公共访问器的私有字段,以补充封闭化。

        getter 和 setter 的原理是为类成员的输入输出值提供验证。然而,在 OOP 语言中,我们操作的是对象而不是类。基类和专用类代表一个对象,这就是为什么通过受保护字段访问特定类成员是完全可以的。

        考虑以下关于汽车的抽象示例: - 你有一个基类 Car 和派生类 Porshe。 - Car 类可能有类似 engine 的字段,其值未在 Cars 构造函数中设置(可能只有在对象初始化后才知道引擎的类型) - 您创建一个 Porshe 类对象,其中包含一些用于使用一些外部数据计算 engine 类型的逻辑。

        在这个例子中,engine 字段应该有一个公共的 getter,所以汽车用户知道汽车有什么引擎。但是,没有公开的二传手,因为我们希望汽车司机不要对引擎发脾气!这就是为什么将 engine 设置为受保护字段是完全可以的,因此 Porshe 类可以在将来的某个时间设置它的值。

        是的,有些人可能会说“然后使用受保护的设置器!”。 我再重复一遍:在 OOP 语言中,我们使用对象而不是类。 单一责任原则 - 是的,但作为对象而不是作为类。 如果您说:“在某些时候,如果我们使用超过 3 或 5 个继承级别的受保护字段,那么如果每个类都对其执行某些操作,则可能很难理解该字段会发生什么”。然后我回答:这是另一种反模式——此时您的对象可能太大了,导致单一责任原则无效。

        【讨论】:

          【解决方案8】:

          从子类访问受保护的字段是继承违反封装的方式之一。出于这个原因,使用公共 API 会更好。

          【讨论】:

            猜你喜欢
            • 2012-02-13
            • 1970-01-01
            • 2013-03-31
            • 2011-06-19
            • 2013-07-08
            • 1970-01-01
            • 2013-03-13
            • 2011-09-02
            • 2015-05-16
            相关资源
            最近更新 更多