用getter和setter调用实例属性好不好?

Is it good to call instance attributes using getter and setter?

我正在制作一款战舰 java 游戏,在整个过程中我都使用点 class 来存储 X 和 Y 坐标。我知道 getter 和 setter 如何工作以及我们为什么使用它们,但是这个特定的 class 同时具有 getter 和 public 属性所以这是我的问题,使用什么更好?

p.x 或 p.getX()

我知道使用 getter 是一种很好的做法,但在这种情况下我很困惑,我应该使用什么?

那个点 class 是一个古老的、过时的遗迹,是各种非惯用语 java。最初的错误是首先使用它;您的代码可能与 java.awt 无关,因此使用该包中的 class 并不是一个好主意。即使是,class 在这一点上只是一个错误,但不幸的是它的文档没有指出这一点。

鉴于已经犯了错误,问题'which one of these 2 things are better style'基本上没有实际意义:两者都不好。

如果您必须在这里抛硬币,请选择 .getX(),它至少看起来更地道。

如果你有时间,自己做点class。它应该看起来像:

@Value public class Point {
    int x, y;
}

public record Point(int x, y) {
}

public class Point {
  private final int x, y;

  public Point(int x, int y) {
    this.x = x;
    this.y = y;
  }

  public int getX() {
    return x;
  }

  public int getY() {
    return y;
  }

  @Override public int hashCode() {
    return (x * 31) ^ y;
  }

  @Override public boolean equals(Object other) {
    if (other == this) return true;
    if (!(other instanceof Point)) return false;
    Point p = (Point) other;
    return p.x == x && p.y == y;
  }

  @Override public String toString() {
    return String.format("[%d, %d]", x, y);
  }
}

考虑到第三个选项中的文字墙,前两个选项中的一个可能更好。第一个使用 Project Lombok 来避免样板,第二个使用一个你不能真正使用的功能,除非你安装了 Java16。

注意:众所周知,我为 Lombok 做出了贡献 :)

NB2:java.awt.Point 不会改变的原因是因为现有代码会有 p.x = 5; 或其他地方,因此,像这样改变它(使其不可变)会破坏现有代码,java 不会这样做,除非有非常强烈的缓解理由。

如果您在设置属性时进行了一些修改,例如散列或加密 ,你应该在 private 中制作这些属性,使用 gettersetter,否则,只需在 public.

中制作你的属性

(回顾一下,这是关于不可变对象与直接改变原始位置值的对比)

我不得不反驳@rzwitserloot 的回答。他提供了三个有问题的 classes:

  1. 第一个,Lombok,要么也是不可变的,要么使用@NonFinal 或@PackagePrivate 注释,因此使用 (creating/allowing) 未经检查的 getter 和 setter。因此,当谈到代码的安全性时,要么存在不可变性问题,要么与(可直接访问的)public 成员变量处于同一级别。
  2. 第二个,记录,浅层不可变。这意味着不能对 Point 本身对位置进行任何更改,但每次都必须创建一个新的 Point 实例
  3. 第三个,与第二个非常相似,是真正不可变的class。同样在这里:对位置的任何更改都不能对 Point 本身进行,但是每次都必须创建一个新的 Point 实例

所以我们在这里看到的是:不变性。如果你想让你的代码干净整洁,不变性很好。特别是调试。不变性是编写干净软件的好工具。修补匠的梦想。但是对于动手能力强的人来说,在某些情况下,这很快就变成了'overengineering'。

但是在性能方面存在一些问题:

  1. 对于不可变 class 的每次更改,您都必须创建该 class 的副本,并进行一些更改
  2. 很难只更改一个 aspect/value,然后在不创建多个对象的情况下更改另一个值

所以,假设一个愚蠢的 JVM,

  1. 您很快就会 运行 陷入基于内存的性能问题,因为您需要创建所有这些对象
  2. 速度比简单地更改值要慢得多。

很高兴,JVM 非常聪明:

  1. 在短暂的 运行 时间后,JIT finds/knows class 被使用和丢弃并对其进行优化。
  2. 此外,通过 getters/setters 访问要慢很多,但在大多数情况下,JVM 也可以消除其中的大部分。

此外,随着 JVM 每年都在进步,optimization/speed 可能会进一步增加,从而减少甚至消除不可变 classes 的任何缺点。此外,优化肯定会更安全,或者至少与您设计的任何东西一样安全。但它们能同样高效吗?

BUT 也有 JVM 无法进行任何优化的情况。尤其是当涉及 interfaces/abstract base classes 时,即使是方法调用也会变慢,因为真正的目标方法的地址必须在 运行 时间解析,每一次。

所以最后,由您来测试并决定要使用哪种方法。 如果这一点额外的安全性真的值得 'drop' 的性能。 关于 JVM 优化,您对未来有多少期望。

其他人说得对的地方:不要使用 AWT classes 除非你真的在使用 AWT。最好有自己的 class 来满足您的需求。

// 大更新:

最后要考虑的一件事,IMO 是使用不可变 Position 类型的最大缺点:

(警告:这个例子会变得越来越荒谬!但它显示了该策略的导向)

  • 假设我们有以下 Class:class Ship { Position position; }
  • 所以我们通过 ship.position
  • 解决这个问题
  • 现在,当我们想要改变船的位置时,我们必须改变对位置的引用:ship.position = ... (new Position or ship.position.clone(newX,newY) or ship.position.with(newX,newY)
  • 所以每当我们想改变船的位置,并遵循不变性模式
    • 半结果:我们至少需要另一个 getter/setter 作为船上的位置
      • 任何处理位置的东西也必须知道它包含 Classes,例如 Ship 和任何和所有其他有位置的东西并且可能由相同的逻辑计算(是的,接口,但是那在哪里停止?)
      • 此外,您还必须每次检查 Ship.position 是否不为空...
    • 完全因此:船舶也应该是不可变的,并且在其位置或任何其他状态发生任何变化时,场景中的船舶将被一个静音的不可变副本所取代。
      • 此外,如果发生任何更改,都必须替换方案。哇
        • 任何使用飞船的东西都必须知道飞船的所有引用,所以它可以更新那些
        • 如果场景发生变化,游戏也应该是不可变的并且它的实例必须被替换..等等,什么?
      • 所以我们看到存在很多不可变对象不是好的解决方案的场景