Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/341.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 使类不可变的另一种方法_Java_Immutability - Fatal编程技术网

Java 使类不可变的另一种方法

Java 使类不可变的另一种方法,java,immutability,Java,Immutability,我在读有效的java第三版。主题是 项目17:最大限度地减少相互性 在本主题中,将讨论不可变对象,以及不使类成为最终类但仍然是不可变的替代方法 以下是主题中的内容: 回想一下,为了保证不变性,类不能允许自己被子类化。这可以通过期末考试来完成,但还有另一个更灵活的选择。private或package private并添加公共静态工厂来代替公共构造函数 我可以同意将构造函数设为私有,这样会使类无法被子类化。但是带有包私有构造函数的类可以在包中使用子类。那么,这个类是否仍然可以使用包私有构造函数保持不

我在读有效的java第三版。主题是

项目17:最大限度地减少相互性

在本主题中,将讨论不可变对象,以及不使类成为最终类但仍然是不可变的替代方法

以下是主题中的内容:

回想一下,为了保证不变性,类不能允许自己被子类化。这可以通过期末考试来完成,但还有另一个更灵活的选择。private或package private并添加公共静态工厂来代替公共构造函数

我可以同意将构造函数设为私有,这样会使类无法被子类化。但是带有包私有构造函数的类可以在包中使用子类。那么,这个类是否仍然可以使用包私有构造函数保持不变

编辑1:

具有包私有构造函数的类对于包外部的类仍然是不可变的。但这种方法有用吗

public class BaseClass {
   private final String arg1;
   private final String arg2;

   public BaseClass(final String arg1, final String arg2) {
      this.arg1 = arg1;
      this.arg2 = arg2;
   }

   public String getArg1() { return arg1; }
   public String getArg2() { return arg2; }
}
可以对此类进行子类化,但必须始终提供
arg1
arg2
的值,并且不能更改。显然,通过对它进行子类化,您可以覆盖两个
getter
方法

为了避免我和同事可能犯的错误,我的方法是对
接口进行编程

在整个代码库中使用此接口将确保减少错误数量,因为内部状态可能仅在创建点更改。一旦对象脱离了创建点,就不能再对其进行更改(当然可以,但您必须手动向下转换它,这很容易发现)



另一种避免子类化(以及可能的易变性)的策略是使用注释处理器。这将在编译时强制执行您的规则。

拥有包专用构造函数并不能保证100%的不变性,正如注释中已经提到的:

可以在同一个包中创建可变子类

开发
jar
库您可能会在团队和软件包维护人员之间达成一些协议,但是couse没有以编程方式实施这一点,但仍然在产品开发人员的控制之下

其他人呢?可以在库之外创建类,但可以在同一个包中以及类路径中的多个位置创建类

如果你有这样的情况,并且关心这样的问题,就开始发挥作用

JAR文件中的包可以选择密封,这意味着 该包中定义的所有类都必须归档在同一个JAR中 文件例如,您可能希望密封一个包,以确保 软件中类之间的一致性

如果要保证包中的所有类都来自 相同的源代码,使用JAR密封。密封的JAR指定所有 该JAR定义的包是密封的,除非在 以每包为基础

假设您的
jar
libarary有一个类
com.example.Factory
protected/package private
成员,包
com.example
是密封的。创建试图访问这些成员的类
com.example.Accessor
的尝试应失败

这就是如何确保有限(且可能受信任)的成员组访问
包私有
成员的方法

回到主要问题:

那么,这个类是否仍然可以使用包私有构造函数保持不变


由于维护人员之间达成了强有力的协议,仔细的代码审查和包密封,它可能会变得不可更改。

不一定会这样,不可以。可以在同一个包中创建一个可变子类。如果包维护者主要关心包+1的用户不进行父级化,那么他们可能会认为这是一个足够好的不变性实施。另一方面:immutable for me在某种程度上与将某物放入容器并向用户发出检索这些东西:“停止,除非你确切知道你在做什么,否则不要改变这个东西。”我认为你忽略了这种情况,只解释子类化。“你可以维护一个类不可变,同时允许子类化。”如果你想保留大多数用户认为最有用的不可变特性,就不能这样做。如果我读到一个类是不可变的,我通常希望能够假设它的状态没有任何方面会改变。我可以缓存它,只要我喜欢,它会很好。如果创建了一个可变的子类,状态可以在我的代码脚下更改-即使它是我从未使用过的类的一个方面,如果我传递对对象的引用,希望它是不可变的,这可能会导致问题。@JonSkeet你是对的。我指定了子类化时可以更改的行为(但不是数据)。这就是为什么我还编写了“程序到接口”,以便随着时间的推移减少错误的数量。如果代码不是您的(可能来自另一个库),那么注释处理器的想法也不错,可以强制执行特定的规则。如果您甚至没有最终确定
getArg1()
方法,那么对于这些方法,它甚至不是不可变的。这不是我认为不可变的,而且它肯定没有保留不可变的许多好处。编程到接口是很好的,但这是另一种情况,在这种情况下,你真的不能保证不变性——而这通常是一件非常有用的事情。问题是关于不变性的——你建议的选项不能保证不变性。@JonSkeet当然!那么,我将不得不从答案中删除“不可变”一词,因为我可能误用了它。
public interface BaseInterface {
   String arg1();
   String arg2();
}