微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

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

如何解决如何从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 之前,创建一个类,您可以:
    • 标记所有成员字段 finalprivate
    • 根据需要创建 getter 方法,但不要创建 setter 方法。

记录

只需在 Java 16 (records feature) 中使用新的 previewed in Java 15

当它的主要工作是不变地携带数据时,将您的类定义为 record。编译器隐式地创建了一个构造函数、getter、hashCode & equalstoString

请注意,记录中隐式定义的 getter 方法JavaBeans-style get… 措辞开头。 getter 方法只是在类名后面的括号中定义的成员字段的名称。

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

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… 命名。

您还应该提供 hashCodeequalstoString 的适当覆盖实现。您的 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 类,也不要使用 CalendarSimpleDateFormat。这些糟糕的类现在已经成为遗留类,多年前被 JSR 310 中定义的现代 java.time 类所取代。在上面的代码中,我们使用 java.time.LocalDate 表示仅日期值,没有时间,没有时区。

,

Planet 是不可变的,但字段 name 应该是私有的。

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。