是否存在可以“终结”对象私有状态的Java方法?
我有一个课程,我想执行以下操作:
- 创建类的实例
- 在单独的类中更改对象的私有属性
- 冻结对象的属性,以便在我完成初始设置后无法修改它们
我知道有一些解决方法,例如不提供任何 setter 方法,只允许在构造函数中设置属性。我可以毫无问题地实现这一点,但它让我想知道是否有一些更简单的方法可以将对象的属性“冻结”到位。我相信Object.freeze()JavaScript 中有一种方法可以做类似的事情。
回答
不
不,Java 不提供任何此类解冻/冻结功能。
建造者
您可能可以通过“建设者”满足您的需求。这个想法是您定义第二个类来负责生成您的第一个类的实例。这个构建器类具有用于您要调整的所有各种属性的 setter 方法。完成所有设置后,您调用.build()以生成所需类的实例。如果您愿意,那么所需的实例可能是不可变的。
构建器应该从其 setter 返回对自身的引用,以提供方法链。
如果在您的问题域中适用,构建器可以为某些设置设置默认值。
builder 类提供了一些好处:
- 实现您的目标,即生成一个对象,该对象只有在调整其预期值后才能保持不变。
- 确保有效值。调用程序员可以使用
isValid方法验证构建器,然后更正设置。 - 允许在适当的情况下返回子类,具体取决于您设置的值。
您可以在与 Java 捆绑的 API 中看到此类构建器。
Locale.Builder有设置语言、地区、脚本等的方法。您最终会调用Locale.Builder#build以生成一个Locale对象。DateTimeFormatterBuilder有许多设置器,用于对所需日期时间格式化程序的许多方面进行细微控制。调用toFormatter(与build方法相同的想法)以生成DateTimeFormatter.
例子
这是一个构建器的简短示例。
如果需要一个不可变的对象,那么 arecord可能是合适的。在 Java 16+ 中,记录是定义类的一种简短方式,其主要目的是透明且不可变地通信数据。您只需声明其成员字段的类型和名称。编译器隐式地创建默认构造函数、getter、equals&hashCode和toString。
public record Employee( UUID id , String name , LocalDate hired ) {}
虽然记录是一种特殊的类,但它仍然是一个类。所以我们可以嵌套另一个类,一个static构建器类。
public record Employee( UUID id , String name , LocalDate hired ) {
public static class Builder { … }
}
这是整个示例类。
package work.basil.building;
import java.time.LocalDate;
import java.util.Objects;
import java.util.UUID;
public record Employee( UUID id , String name , LocalDate hired ) {
public static class Builder {
// ----------- Members
private UUID id;
private String name;
private LocalDate hired;
// ----------- Constructor
public Builder () {
this.id = UUID.randomUUID();
}
// ------- Accessors
public UUID getId () {
return id;
}
public Employee.Builder setId ( UUID id ) {
this.id = Objects.requireNonNull( id );
return this;
}
public String getName () {
return name;
}
public Employee.Builder setName ( String name ) {
Objects.requireNonNull( name );
if ( name.isBlank() ) {
throw new IllegalStateException( "Name must have some text, cannot be blank. Message # 346624fd-cb97-447a-9f56-e09ccf2e97f3." );
} else {
this.name = name;
}
return this;
}
public LocalDate getHired () {
return hired;
}
public Employee.Builder setHired ( LocalDate hired ) {
Objects.requireNonNull( hired );
if ( hired.isAfter( LocalDate.now() ) ) {
throw new IllegalStateException( "Hired date cannot be after today. Message # 181717b8-e2b0-4b5c-9fd2-ee45a2339b09." );
} else {
this.hired = hired;
}
return this;
}
// -------- Logic
public boolean isValid () {
return Objects.nonNull( this.id ) && Objects.nonNull( this.name ) && Objects.nonNull( this.hired );
}
public Employee build () {
if ( this.isValid() ) {
return new Employee( this.id , this.name , this.hired );
} else {
throw new IllegalStateException( "Builder is not valid, so cannot build new object. Message # c0021179-243c-4da5-b265-85208aaaf072" );
}
}
}
}
示例用法。
List <Employee> employees =
List.of(
new Employee.Builder().setName( "Alice" ).setHired( LocalDate.of( 2018 , Month.MARCH, 23) ).build() ,
new Employee.Builder().setName( "Bob" ).setHired( LocalDate.of( 2014 , Month.JANUARY, 28) ).build() ,
new Employee.Builder().setName( "Carol" ).setHired( LocalDate.of( 2013 , Month.JUNE, 17) ).build()
);
跑的时候。
雇员 = [雇员 [id=9736cb4c-1b32-4924-976b-7340f7f2fdc4, 姓名=爱丽丝, 雇用=2018-03-23], 雇员 [id=0ac4ff54-51b6-45c9-bb57-59f6efe4B, 雇用2014-01-28],员工[id=52cc9d03-3846-464a-bbed-49f022175bee,姓名=卡罗尔,受雇=2013-06-17]]
- 在这种情况下,我认为您在错误的意义上使用了“short of”。在这种情况下,您使用“short of”是说如果您使用反射,您*可以*实现冻结/解冻机制。