java中如何从可变对象创建不可变对象?

ric*_*kyk -1 java oop access-modifiers immutability

如何创建不可变的星球以便名称不会改变?我很挣扎,因为我认为这是具有可变对象的不可变项目。如果我错了请纠正我。

每次我更改输出中的名称也会更改。我错过了什么吗?

我尝试将所有字段设为私有和最终(不在本例中),但我认为我缺少一些工作代码。

我知道 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());
    }
}
Run Code Online (Sandbox Code Playgroud)

Bas*_*que 5

太长了;博士

\n

任何一个:

\n
    \n
  • record在 Java 16 及更高版本中进行如下操作:
    public record Planet( String name , LocalDate discovered ) {}
  • \n
  • 或者,在 Java 16 之前,创建一个类,其中您:\n
      \n
    • 标记所有成员字段finalprivate
    • \n
    • 根据需要创建 getter 方法,但不创建 setter 方法。
    • \n
    \n
  • \n
\n

记录

\n

只需使用Java 16 中的新记录功能(在 Java 15 中预览)。

\n

将您的类定义为 a record,其主要工作是透明且不可变地携带数据。编译器隐式创建一个构造函数、getter、hashCode&equalstoString

\n

请注意,记录中隐式定义的 getter 方法不JavaBeans风格的措辞开头get\xe2\x80\xa6。getter 方法只是类名后面括号中定义的成员字段的名称。

\n

当然,如果您的 getter 方法提供对本身可变的对象的访问,则包含在记录中并不会阻止调用程序员改变所包含的对象。请注意,在接下来的示例类中,StringLocalDate类本身在设计上都是不可变的。因此,所包含对象的可变性在这里不是问题。

\n
package org.example;\n\nimport java.time.LocalDate;\n\npublic record Planet( String name , LocalDate discovered )\n{\n}\n
Run Code Online (Sandbox Code Playgroud)\n

使用该记录。

\n
Planet Earth = new Planet( "Earth" , LocalDate.of( 2020 , 1 , 16 ) );\n\nSystem.out.println( "Earth" );\nSystem.out.println( "------------------------------------" );\nSystem.out.println( "Earth.name: " + Earth.name() );\nSystem.out.println( "Earth.discovered: " + Earth.discovered() );\n
Run Code Online (Sandbox Code Playgroud)\n

运行时。

\n
Earth\n------------------------------------\nEarth.name: Earth\nEarth.discovered: 2020-01-16\n
Run Code Online (Sandbox Code Playgroud)\n

班级

\n

如果没有记录功能,要确保类不可变,您应该:

\n
    \n
  • 标记成员字段final。这意味着构造函数完成后不能为该字段分配不同的对象。
  • \n
  • 标记成员字段private。这意味着其他类的对象将无法直接访问读取或更改这些字段。
  • \n
  • 如果需要,提供 getter 方法,但不提供 setter 方法。按照惯例,使用JavaBeans 样式get\xe2\x80\xa6或命名。is\xe2\x80\xa6
  • \n
\n

您还应该提供hashCodeequals和的适当覆盖实现toString。您的IDE将帮助生成这些的源代码。

\n
package org.example;\n\nimport java.time.LocalDate;\nimport java.util.Objects;\n\npublic class Plan\xc3\xa8te\n{\n    // Member fields\n    final String name;\n    final LocalDate discovered;\n\n    // Constructors\n    public Plan\xc3\xa8te ( String name , LocalDate discovered )\n    {\n        Objects.requireNonNull( name );\n        Objects.requireNonNull( discovered );\n        this.name = name;\n        this.discovered = discovered;\n    }\n\n    // Getters (read-only immutable class, no setters)\n    public String getName ( ) { return this.name; }\n\n    public LocalDate getDiscovered ( ) { return this.discovered; }\n\n    // Object class overrides\n    @Override\n    public boolean equals ( Object o )\n    {\n        if ( this == o ) return true;\n        if ( o == null || getClass() != o.getClass() ) return false;\n        Plan\xc3\xa8te plan\xc3\xa8te = ( Plan\xc3\xa8te ) o;\n        return getName().equals( plan\xc3\xa8te.getName() ) && getDiscovered().equals( plan\xc3\xa8te.getDiscovered() );\n    }\n\n    @Override\n    public int hashCode ( )\n    {\n        return Objects.hash( getName() , getDiscovered() );\n    }\n\n    @Override\n    public String toString ( )\n    {\n        return "Plan\xc3\xa8te{ " +\n                "name=\'" + name + \'\\\'\' +\n                " | discovered=" + discovered +\n                " }";\n    }\n}\n
Run Code Online (Sandbox Code Playgroud)\n

使用那个类。

\n
Plan\xc3\xa8te Earth = new Plan\xc3\xa8te( "Earth" , LocalDate.of( 2020 , 1 , 16 ) );\n\nSystem.out.println( "Earth" );\nSystem.out.println( "------------------------------------" );\nSystem.out.println( "Earth.getName: " + Earth.getName() );\nSystem.out.println( "Earth.getDiscoveryDate: " + Earth.getDiscovered() );\n
Run Code Online (Sandbox Code Playgroud)\n

附带问题

\n

不要以 开头十进制整数文字0。前导零使数字成为八进制而不是十进制。所以你的代码传递2020,01,16应该是2020,1,16.

\n

切勿使用该类Date,也不要使用CalendarSimpleDateFormat。这些可怕的类现在已经成为遗留物,几年前被JSR 310 中定义的现代java.timejava.time.LocalDate类所取代。在上面的代码中,我们用来表示仅日期值,没有日期时间和时区。

\n