如何解决如何从java中的可变对象创建不可变对象?
如何创建不可变的 Planet 以便名称不会改变?我很挣扎,因为我认为它是具有可变对象的不可变项目。如果我错了,请纠正我。
我尝试将所有字段设为私有和最终字段(不在此示例中),但我想我缺少一些代码来工作。
我知道 java.util.Date 已被弃用,但这只是举例。
import java.util.Date;
public final class Planet {
String name;
private final Date discoveryDate;
public Planet (String name,Date discoveryDate) {
this.name = name;
this.discoveryDate = new Date(discoveryDate.getTime());
}
public String getName()
return name;
}
public Date getdiscoveryDate() {
return new Date(discoveryDate.getTime());
}
public static void main(String [] args) {
Planet Earth = new Planet("Earth Planet",new Date(2020,01,16,17,28));
System.out.println("Earth");
System.out.println("------------------------------------");
System.out.println("Earth.getName: " + Earth.getName());
System.out.println("Earth.getdiscoveryDate: " + Earth.getdiscoveryDate());
}
}
解决方法
tl;博士
要么:
- 在 Java 16 及更高版本中制作这样的
record
:public record Planet( String name,LocalDate discovered ) {}
- 或者,在 Java 16 之前,创建一个类,您可以:
- 标记所有成员字段
final
和private
。 - 根据需要创建 getter 方法,但不要创建 setter 方法。
- 标记所有成员字段
记录
只需在 Java 16 (records feature) 中使用新的 previewed in Java 15。
当它的主要工作是不变地携带数据时,将您的类定义为 record
。编译器隐式地创建了一个构造函数、getter、hashCode
& equals
和 toString
。
请注意,记录中隐式定义的 getter 方法不以 JavaBeans-style get…
措辞开头。 getter 方法只是在类名后面的括号中定义的成员字段的名称。
当然,如果您的 getter 方法提供对本身可变的对象的访问,则包含在记录中并不能阻止调用程序员改变所包含的对象。请注意,在接下来的示例类中,String
和 LocalDate
类本身在设计上都是不可变的。因此,包含对象的可变性在这里不是问题。
package org.example;
import java.time.LocalDate;
public record Planet( String name,LocalDate discovered )
{
}
使用该记录。
Planet Earth = new Planet( "Earth",LocalDate.of( 2020,1,16 ) );
System.out.println( "Earth" );
System.out.println( "------------------------------------" );
System.out.println( "Earth.name: " + Earth.name() );
System.out.println( "Earth.discovered: " + Earth.discovered() );
运行时。
Earth
------------------------------------
Earth.name: Earth
Earth.discovered: 2020-01-16
班级
如果没有记录功能,要确保类是不可变的,您应该:
- 标记成员字段
final
。这意味着在构造函数完成后不能为该字段分配不同的对象。 - 标记成员字段
private
。这意味着其他类的对象将无法直接访问读取或更改这些字段。 - 如果需要,提供 getter 方法,但不要提供 setter 方法。按照惯例,使用 JavaBeans 样式的
get…
或is…
命名。
您还应该提供 hashCode
、equals
和 toString
的适当覆盖实现。您的 IDE 将帮助生成这些源代码。
package org.example;
import java.time.LocalDate;
import java.util.Objects;
public class Planète
{
// Member fields
final String name;
final LocalDate discovered;
// Constructors
public Planète ( String name,LocalDate discovered )
{
Objects.requireNonNull( name );
Objects.requireNonNull( discovered );
this.name = name;
this.discovered = discovered;
}
// Getters (read-only immutable class,no setters)
public String getName ( ) { return this.name; }
public LocalDate getDiscovered ( ) { return this.discovered; }
// Object class overrides
@Override
public boolean equals ( Object o )
{
if ( this == o ) return true;
if ( o == null || getClass() != o.getClass() ) return false;
Planète planète = ( Planète ) o;
return getName().equals( planète.getName() ) && getDiscovered().equals( planète.getDiscovered() );
}
@Override
public int hashCode ( )
{
return Objects.hash( getName(),getDiscovered() );
}
@Override
public String toString ( )
{
return "Planète{ " +
"name='" + name + '\'' +
" | discovered=" + discovered +
" }";
}
}
使用那个类。
Planète Earth = new Planète( "Earth",16 ) );
System.out.println( "Earth" );
System.out.println( "------------------------------------" );
System.out.println( "Earth.getName: " + Earth.getName() );
System.out.println( "Earth.getDiscoveryDate: " + Earth.getDiscovered() );
附带问题
不要以 0
开始十进制整数文字。前导零使数字 octal 而不是 decimal。所以你传递 2020,01,16
的代码应该是 2020,16
。
切勿使用 Date
类,也不要使用 Calendar
或 SimpleDateFormat
。这些糟糕的类现在已经成为遗留类,多年前被 JSR 310 中定义的现代 java.time 类所取代。在上面的代码中,我们使用 java.time.LocalDate
表示仅日期值,没有时间,没有时区。
Planet 是不可变的,但字段 name
应该是私有的。
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。