如何检查一个对象的不可变性和可变性

3
在项目的规格说明和要求中提到,产品ID不应该可更改,它是Long类型。这是否意味着它必须是不变的?如果是,那么如何通过编程来检查对象的不变性?

“这是否意味着它必须是不可变的?”:“是的。” “如何检查对象是否是不可变的?”:“您是指通过编程还是通过设计?” - Arthur Attout
@ArthurAttout 通过编程方式,我修改了问题编号。 - user2121
通常,检测对象实例是否不可变有两种不同的路径; 在编译时或运行时。目前,Java的设计和平台并没有真正实现在编译时进行检测。我创建了一个非常小的开源库,可以在运行时检测它。我将一般解决方案描述为StackOverflow答案(其中包含链接到Github发布代码的链接),位于此处:https://dev59.com/IloU5IYBdhLWcg3wzZLw#75043881 - chaotic3quilibrium
4个回答

2
这是一段关于对象是否不可变的讨论。"not changeable"和"immutable"是同义词。如果一个对象没有提供任何改变自身状态的操作(例如setters等),那么它就是不可变的。你需要查看文档和对象类的API来确定它是否不可变。该类还需要是final或没有受保护的字段,否则您可以对其进行子类化并修改受保护的字段。举例来说,Long是不可变的,因为它没有提供任何更改状态的方法,并且它是一个final类。
在这个答案发布后,您修改了问题为:
“如何以编程方式检查对象是否不可变。”
我认为你不能。反射中没有任何东西告诉你一个方法是否是改变器,基于方法名称进行假设是极其不可靠的。当然,如果对象的类是final的,并且根本没有实例方法,那可能意味着它是不可变的。但许多不可变的类都有实例方法(包括Long)。

我们可以通过编程的方式检查吗?有没有相应的方法可以这样做? - user2121
1
@user2121 - 我不这么认为。我看不出你为什么需要这样做。 - T.J. Crowder
@user2121:如果你在寻找一个简单的标志或类似的东西,那是不存在的。但是,大多数手动检查的事情都可以通过编程方式进行检查,但通常不值得这样做(因为大多数时候,您想要验证整个类型是否不可变,而不仅仅是特定实例,有时无法以编程方式完成(例如,List字段可能是不可变的,也可能是可变的,这取决于它所引用的内容)。 - Joachim Sauer

1

是的,您可以确定一个对象是否是不可变的。然而,这是非常复杂的业务,因此解决方案无法适用于 Stack Overflow 上的答案。

有两种方法可以确定一个类是否是不可变的:

  • 通过静态分析
  • 通过运行时检查

现有的静态分析工具是 MutabilityDetector(https://github.com/MutabilityDetector)。

现有的运行时检查工具是 Bathyscaphe(https://github.com/mikenakis/Bathyscaphe)(我是作者)。

在我看来,静态分析在某些情况和特定场景下是有用的,但并不总是能够提供完整的解决方案。完整的解决方案需要运行时检查,正如我在我的博客中所解释的那样:https://blog.michael.gr/2022/05/bathyscaphe.html,这也是为什么我编写了Bathyscaphe。

这是一个非常好的解决方案。它非常接近我在StackOverflow答案中描述的解决方案,链接在这里:https://dev59.com/IloU5IYBdhLWcg3wzZLw#75043881。 - chaotic3quilibrium
我刚刚将你的答案添加到我的答案中。我这样做是因为在这个领域的需求可能会非常多样化。很可能你的解决方案对于这整个领域的某些子集至少比我的更优秀。也许甚至全部都是。如果我们提供足够的开源选项,只会有助于整个Java社区。再次感谢您创建解决方案。 - chaotic3quilibrium
1
@chaotic3quilibrium,我看了你的代码,必须承认我不明白它是如何工作的。你愿意通过邮件交流并谈谈吗?(可以通过电子邮件或者视频进行)。在我的Stack Overflow个人资料中有找到我的电子邮件地址的说明。 - Mike Nakis
1
@chaotic3quilibrium,我会阅读这篇文章https://dev59.com/IloU5IYBdhLWcg3wzZLw#75043881,希望我能理解。但是,我们仍然可以在未来某个时候讨论它,因为我们都对此有共同的兴趣。 - Mike Nakis
1
我很想视频聊天。我会在你的电子邮件上联系你。 - chaotic3quilibrium

0
问题不在于'Long'是否不可变,而在于'Product'的id是否不可变。
通过将id字段设置为私有,不实现任何更改id的方法,并且不编写更改id的Product类代码,可以提供id的不可变性。
为什么Long的不可变性不相关呢?考虑以下未能满足您要求的示例:
class Product {
    private Long id;
    Product() { id = 42; }
    Long getId() { return id; }
    void setId(Long newId) { id = newId; }
       :
}

所有这些Long都是不可变的,但产品ID仍然会发生更改:因为setId将一个不同的不可变Long值分配给id成员。

然而,如果您只删除那个setId方法(并且在Product类中不编写任何类似的内容),则产品ID现在是不可更改的。


0
如何在编程中以编程方式检查对象是否是不可变的? 我们无法这样做。此外,我认为这不一定要以编程方式完成。请继续阅读, 让我们假设有一个如下所示的虚构“异常”类,
public class MutationOfImmutableException extends Exception {

  /**
   * 
   */
  private static final long serialVersionUID = 1L;

  public MutationOfImmutableException(String string) {
    super(string);
  } 

}

我们的类(我想不出一个好名字,但我想这传达了这个想法)有

public void setName(String name) throws MutationOfImmutableException {
    throw new MutationOfImmutableException("Cannot mutate this immutable instance");
}

在我们的主类中,每当有人错误地调用了 setName() 方法时,编译器都会警告我们要正确处理异常。
p.setName("Anees"); // Cannot do this until handled!

简而言之,如果您可以访问不可变的类Product,则可以从setId()抛出一个已检查异常,以避免一切混乱!
更好的方法是将您的setters设置为私有,这样您甚至不会遇到这种情况。

3
如果 setter 不能被使用,为什么要有它? - user10762593

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