【问题标题】:Why use private modifier for immutable final instance var?为什么对不可变的最终实例变量使用私有修饰符?
【发布时间】:2012-11-26 01:09:01
【问题描述】:

我正在编写一个表示一些简单几何图形的 Java 类。

在最顶层的abstract 类(它本身是package-private)我声明了我需要从同一个包中的子类访问的属性。

如果我在AbstractClass 中将属性声明为final

final int foo;

我将能够直接在包中访问它,而无需大惊小怪的 getter 方法。 然而。按照“实践”(或者我认为是常见的风格)做的会是:

private final int foo;

这当然需要非private getter。子类必须引用foo(这是一个非常相关且典型的属性),就好像它是某个外部对象一样:

this.getFoo();

它添加代码并删除访问这些成员的直接方式(即foo)。

跳过private 修饰符有什么缺点,因为它们无论如何都是最终的,我不担心在包内部公开这些属性?

我知道 OO 拥护者声称 getter/setter 是对象访问其自身属性的一种非常自然的方式 - 但是这何时会使任何非装饰性、非 [插入任何 JavaBeans 样式的东西],区别?

考虑一个内部类Coordinate,它非常简单,因为它有两个int 属性 - 将所有用法留给OuterClass 类:

class OuterClass{
    final static class Coordinate{
        final int x, y;
        Coordinate(int x, int y){
            this.x = x;
            this.y = y;
        }
    }
    Coordinate coordinate;
}

对于这个内部类 - 我为什么要为创建 getter 的实践而烦恼? getter 会引入更多代码并强制同一包中的任何类调用coordinate.getX(); 而不是简单的coordinate.x;。这里有开销吗?注意 Coordinate 类上的 final 修饰符。

【问题讨论】:

  • 如果您允许从对象外部对对象的字段进行任何访问,有些人会变得很热和烦恼。对这些人来说,这很重要。对其他人来说,不。
  • @HotLicks :这实际上是我最关心的问题 :) atm 对于我的实现它当然没有意义。但我也渴望根据所有优秀的程序员实践进行相应的编码。我的意思是,因为它是最终的,所以可以访问(但是包私有)只是查看变量,而不是使用 getter - 这怎么可能是错误的?特别是在直接看时会产生不那么难看的括号。
  • 担心如果类的内部结构随时间发生变化,可能需要删除直接访问并改用访问器方法。在某些情况下这是一个现实的问题(例如,广泛使用的应用程序的主要公共接口),但在大多数情况下几乎没有问题。直接方法的简单性(因此错误更少)可能很容易超过这种担忧。
  • 内部结构将如何变化?我没有足够的知识来理解它是如何可能的。我的想法是,这个 AbstractClass 通过接口与规范捆绑在一起,因此有它的包私有系统(我定义)和它的公共交互(其他人可以使用)。因此,除了 package-maintaner 之外,没有其他人会受此影响——它很有帮助,因为它使抽象类的扩展更简单(在某种意义上,我可以在子类中通过简单的“foo”而不是 getFoo() 来调用它(即对我来说没有什么意义,因为子类应该感觉 foo 是“他的”)
  • 内部可能会发生变化,例如,如果类被更改为在远程数据库而不是本地数据库上操作,或者应用程序被概括为数字(也许是权重)在您的首选单位(磅与千克),同时内部仍以千克为单位。但在“现实生活”中,无需因其他原因重新设计界面而进行此类更改的几率非常低。

标签: java abstract final


【解决方案1】:

getter 的优点是将接口与实现分离。今天,您的getFoo 可能只会返回foo,但将来您可能希望远程访问foo 成员并返回计算结果。 getter 将允许您这样做,而无需在每个调用站点进行更改。

【讨论】:

  • 问题是这些变量的生态系统包含在最终方法和最终变量的隔离中 - 使得任何更改都不同于现在无用的更改。因此,如果不再需要以任何方式修改该 var 的计算方式,那么就没有理由了吗? (在某种程度上,如果你要求 getX,并且你知道 X 是最终的,那么除了 X 本身没什么可期待的......或者?)
  • 封装的重点与其说是防止未经授权的更改(这确实可以通过将成员标记为 final 来实现),不如说是从接口中解耦实施细节。如果您对此不担心,则没有其他原因。
  • 特别是如果这是一个学习练习,您应该接受封装范式,并确保您完全理解为什么要使用像 getter 和 setter 这样的技术——而不是偷工减料,因为它是一个“真正简单”的项目。学习练习旨在模拟现实世界中的问题,这些问题不像您可以在课堂上解决那么简单,但可以使用相同的方法来解决。
  • 问题是我不确定我是否应该担心。我们应该为几何形式实现一个框架,在某种意义上真的很简单,但任务的重点是“学习如何在更大的编码环境中编码”。更具体地说:我声明了例如最终位置(这是一个描述 2D 平面上的位置的类)。所有放置、移动和更改此变量的方法都是最终的。使这个变量的生命完全在抽象类内部。我只想从子类中对其进行简单的(原生感觉)访问(这就是倾倒吸气剂的原因)。
  • 为什么直接访问属性对您来说是原生的?您是否来自具有活动属性的编程语言,例如 Python?
【解决方案2】:

如果你打算从 JSF 页面访问这个值,它会期望 getFoo 而不仅仅是 foo(即使你写了object.foo)。

除此之外 - 今天这个领域是最终的,从现在起很长一段时间它可以改变(我猜)。即使机会非常接近于 0,我相信遵循良好做法也不会过大。在大多数情况下,您进行更改而不是从头开始编写代码,因此尽可能保护自己要好得多(如果只需要编写 private 并且在 Eclipse 的情况下大约单击 4 次即可自动生成 getter 和 setter )。

【讨论】:

  • 感谢输入和良好提及的互操作性问题,真的没有考虑过。
  • 在开始使用 JSF 和 Java EE 之前,我也从未想过它们。很难预测您编写的代码(如果它不仅仅是一个爱好项目)将被如何使用。此外,如果您与其他程序员合作,他们可能希望遵循这些做法。他们中的一些人开始编写 get... 在他们的 IDE 中寻找弹出的变量。如果您的公共变量不存在,则可以忽略它。编写对与您合作的每个人都友好的代码也很重要。
  • 问题是,它有更多的代码,更多的代码意味着更多的出错机会。特别是对于“次要”属性,发生“重复错误”的几率非常高,而且调试起来可能很昂贵。
【解决方案3】:

扩展 Feldgendler 的答案(提供的信息可能对需要这个问题的答案的人有用 --- 我相信这是相关的,因为它确实是一个关于封装的问题):

使用private 修饰符后,您必须创建“getter”(例如int getX(){ ... })来维持访问权限。可以在已实现的interface 中声明。 Java interface 确实不允许 允许声明实例变量,例如 final int x; --- 或任何其他缺少 static 修饰符的变量的示例。该接口将充当任何实现类将具有的行为的声明。

如果实现如下:

Coordinate implements ICoordinate { ... }

它在许多情况下都可能有用:

界面的使用

  • API 的自文档。
    • 易于阅读、记录和管理。
    • 因此,可以轻松使用和交换多个实现。
    • 在基于组件的设计等方面:提供明确的方法来提供require 描述的行为实际上并没有准备好任何实现代码。
      • 示例:然后可以创建一个提供接口的数据库组件。 John Doe 想要编写一个将来会使用数据库的程序。然而,现在使用其他一些更简单的存储形式就足够了。为了在这一天到来时不重新编码已经工作的代码,John 可以使用方法void insert( ... ); Object get( ... ); 或者更多方法实现一个接口(可能命名为interface IDatabase)——然后实现他的临时解决方案。这一天到来了,他现在拥有一个实现相同interface IDatabase 的数据库。要切换到新数据库,他可能只需要更改*一行代码*(例如构造函数调用)!
  • 举个例子:http://pastebin.com/vvV2Nck8

使用私有修饰符

  • 阐明了类的意图!就像界面一样,它具有自我记录的性质。 (它在安全方面没有价值)
    • private 修饰符意味着它不会被超出范围访问。或者等价地——只能在一定范围内访问。当然同样适用于public、“package-private”(没有修饰符意味着 package-private)和protected
    • 访问属性不会明确告诉调用者它是什么类型的变量。有一个 getter 但没有 setter 说明了别的东西......
    • 注意:对于常量(例如static final double LIGHT_SPEED),有合理的理由省略getter 并使用public 代替。这只是约定,因为它会清楚什么是常量,什么是具有自己成员的对象。
    • 注意2:如果有兴趣,请阅读final 关键字及其对优化的影响。它们可以用在方法和属性中。 (Does use of final keyword in Java improve the performance? 就是一个例子)

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-20
    • 2017-09-29
    • 2013-06-28
    • 1970-01-01
    相关资源
    最近更新 更多