使用getter和setter调用实例属性好吗



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

p.x或p.getX()

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

(概括一下,这是关于不可变对象与直接更改基元位置值)

我必须反驳@rzwitserloot的回答。他提供了三个有问题的课程:

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

所以我们在这里看到的是:不变性。如果你想让你的代码保持整洁,那么不变性是很好的。特别是用于调试。不可更改性是编写干净软件的好工具。修补匠的梦想。但对于动手的人来说,在某些情况下,这很快就会变得"过于工程化"。

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

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

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

  1. 由于需要创建的所有对象,您很快就会遇到基于内存的性能问题
  2. 速度比简单地改变数值要慢得多

很高兴,JVM非常聪明:

  1. 在短的运行时间后,JIT会发现/知道使用和丢弃的类,并对其进行优化
  2. 此外,通过getters/ssetter进行访问的速度要慢得多,但JVM在大多数情况下也会占用大部分时间

此外,随着JVM每年的进步,优化/速度可能会进一步提高,从而减少甚至消除不可变类的任何缺点。此外,这些优化肯定会更安全,或者至少与您可以设计的任何东西一样安全。但它们能同样高效吗?

但是也有JVM既不能进行优化的情况。尤其是当涉及接口/抽象基类时,即使是方法调用也会变得更慢,因为每次都必须在运行时解析真正的目标方法的地址。

所以,最终,由你来测试和决定,你想使用什么方法。如果这一点额外的安全性真的值得性能的"下降"。以及您对未来JVM优化的期望。

其他人是对的:除非你真的在使用AWT,否则不要使用AWT类。最好有自己的课,你可以适应你的需要。

//重大更新:

最后一件需要考虑的事情,以及IMO使用不可变仓位类型的最大缺点:

(警告:这个例子会变得越来越荒谬!但它表明了这种策略的方向)

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

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

考虑到已经犯了错误,"这两件事中哪一件风格更好"的问题基本上是没有意义的:哪一件都不好。

如果你必须在这里掷硬币,那么选择.getX(),它至少看起来更地道。

如果你有时间,制作你自己的Point类。它应该看起来像:

@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否则无法真正使用的功能。

注:众所周知,我为龙目做出了贡献:)

NB2:java.awt.Point不会改变的原因是,现有的代码在某个地方会有p.x = 5;或其他什么,因此,这样改变它(使其不可变)会破坏现有的代码,而java不会这样做,除非有非常有力的缓解理由

如果在设置属性(如哈希或加密)时进行了一些修改,您应该在private中制作这些属性,使用gettersetter,否则,只需在public中制作您的属性即可。

最新更新