【问题标题】:How to make a class immutable in java with date field in it?java - 如何在Java中使用日期字段使类不可变?
【发布时间】:2015-10-29 01:58:49
【问题描述】:

我创建了一个带有日期字段的不可变类。我如何确保即使日期字段也是不可变的,因为即使您将日期字段设为最终字段,您以后仍然可以为其分配不同的值?

【问题讨论】:

  • 除了建议或暗示对日期使用 getter 的答案之外,请确保您的日期字段是私有的,以便其他类无法修改它。
  • 如果用户将不同的值分配给他们从中获得的日期变量,这不会改变您在班级中的日期值
  • @MadProgrammer True,但是 getter 返回的 Date 不是不可变的。这就是为什么在 getter 中复制它很重要。
  • @McBrainy 是的,那部分是真的,但这不是你的问题所说的;)

标签: java immutability


【解决方案1】:

在您的 getDate() 方法中,返回一个 new Date() 实例,而不是同一个实例。

public Date getDate() {
    // Not correct.
    return this.date; // This will make your class mutable.

    // Instead use, 
    return new Date(this.date.getTime()); // This will make sure your date field cannot be changed.
}

【讨论】:

  • 除此之外,我们还可以在构造函数中执行此操作:public Constructor(Date d){ this.date = new Date(d.getTime());},这样如果对 d 对象进行更改也不会影响原始对象中的日期。
【解决方案2】:

java.time

其他答案在显示修复对象中的值的策略时是正确的。

我还建议您使用现代的 java.time 类,而不是糟糕的遗留类。代替Date,使用Instant。代替Calendar,使用ZonedDateTime

java.time 类被设计为不可变对象。 诸如 plus…minus…to…with 等方法都会产生一个新鲜的物体,保持原样不变。这些类不携带 setter 方法。

额外提示:在您自己的不可变类中,您可能会发现遵循 java.time 类建立的方法命名模式很有用。


关于java.time

java.time 框架内置于 Java 8 及更高版本中。这些类取代了麻烦的旧 legacy 日期时间类,例如 java.util.DateCalendarSimpleDateFormat

Joda-Time 项目现在位于maintenance mode,建议迁移到java.time 类。

要了解更多信息,请参阅Oracle Tutorial。并在 Stack Overflow 上搜索许多示例和解释。规格为JSR 310

使用符合JDBC 4.2 或更高版本的JDBC driver,您可以直接与您的数据库交换java.time 对象。不需要字符串也不需要 java.sql.* 类。

从哪里获得 java.time 类?

ThreeTen-Extra 项目通过附加类扩展了 java.time。该项目是未来可能添加到 java.time 的试验场。您可以在这里找到一些有用的类,例如IntervalYearWeekYearQuartermore

【讨论】:

    【解决方案3】:

    您可以克隆日期对象,使其无法修改

    public Date getDate (){
             return (Date) this.date.clone ();
    }
    

    【讨论】:

    • 此方法返回的所有实例仍然是可变的。
    • this.date 无法更改,因此类将保持不变,尽管您可以更改克隆对象
    【解决方案4】:

    这是Javaimmutable类的简单示例

    public final class Employee{  
        final String pancardNumber;  
        public Employee(String pancardNumber){  
            this.pancardNumber=pancardNumber;  
        }  
        public String getPancardNumber(){  
           return pancardNumber;  
        }
    }
    

    上面的类是不可变的,因为:

    1. 类的实例变量是最终的,即我们不能改变 创建对象后它的值。
    2. 该类是最终类,因此我们无法创建子类。
    3. 没有设置方法,即我们有 无法更改实例变量的值。

    这些点使这个类成为不可变的。对于Date 属性,您可以使用构造函数为每个新对象设置日期并导入org.joda.time.DateTime 类。这是一个比java.util.Date 更好的版本,因为它是不可变的。使用 java.util.Date 会很危险,因为它是一个可变类,我们无法控制调用线程(它可能会修改它)。这是一个例子。

    public final class Bill {
    
        private final int amount;
        private final DateTime dateTime;
    
        public Bill(int amount, DateTime dateTime) {
            this.amount = amount;
            this.dateTime = dateTime;
        }
        public int getAmount() {
            return amount;
        }
        public DateTime getDateTime() {
            return dateTime;
        }
    }  
    

    【讨论】:

    • 为什么我们会担心“调用线程”会修改我们的私有字段?通过吸气剂?您可以像 Codebender 或 Joni 一样做,并在从 getter 返回时有效地克隆 Date。
    • @McBrainy:好点。我同意你的看法。我们可以这样做。
    【解决方案5】:

    这是一个带有不可变 HAS-A 日期对象的 Bean(Class) 示例。

     import java.util.Date;
    
    
    public class MyBean {
    
        private Date date; // Immutable Date Step 1 Make Private
    
        public MyBean(Date date)
        {
             // Immutable Date Step 2 If Set through Constructor then get a specialised (sub class) Date.
            this.date= getImmutableDate(date);  // THIS METHOD RETURNS AN IMMUTABLE DATE
        }
    
        public MyBean(){} // Allow Default Constructor
    
        public Date getDate() {
            return date;
        }
    
         // Immutable Date Step 3- Allow setting of date only once!!
        public void setDate(Date date) {
            if(this.date==null)
            {
                this.date= getImmutableDate(date);  
            }
        }
    
        /* Override all Setter methods of Date class. So even if user gets reference of Date Object it is not the original date object
         * it would be a modified date object whose all setter methods do nothing*/
        private Date getImmutableDate(Date date)
        {
            /* This is an Anonymous Inner Class that extends java.util.Date class, it overrides all the setter methods
             * making the date object IMMUTABLE( i.e setXXX has no effect)
             * */
            date =new Date(date.getTime()){
                private static final long serialVersionUID = 1L;
                @Override
                public void setYear(int year) {}
    
                @Override
                public void setMonth(int month) {}
    
                @Override
                public void setDate(int date) {}                
    
                @Override
                public void setHours(int hours) {}
    
                @Override
                public void setMinutes(int minutes) {}
    
                @Override
                public void setSeconds(int seconds) {}
    
    
    
    @Override
            public void setTime(long time) {}
    
        };
        return date;
    }
    

    }

    【讨论】:

    • 最简单的方法是使用字符串。但是如果你想要“java.util.Date”那么上面的例子就可以了
    • 像这样在设置器中静默失败似乎是一个等待发生的错误......@Oliver 以毫秒为单位的时间会比字符串好,但日期是最好的。
    • @McBrainy 您认为这段代码会如何失败?请给它更多的启示。
    • @Oliver,如果您重写了所有这些方法,使用您的 API 的人会感到非常困惑。在许多情况下,这可能不是一个好的编程习惯。
    • @Codebender 这行得通。我认为使用 Joda API 是一个更好的选择,因为 DateTime 对象无论如何都是不可变的。但是,如果您不想使用像 Joda 这样的完整 API,这可能是一个很好的快速解决方案。
    【解决方案6】:

    使用防御性复制。这意味着如果您必须将日期传递给其他类,例如通过方法参数或从方法返回,您应该制作一个副本。

    复制日期很容易:

    new Date(myDate.getting())
    

    【讨论】:

      【解决方案7】:

      识别可变实例变量(如日期或哈希图)返回具有所有可变对象的复制内容的新对象。不可变变量可以安全返回,无需额外努力。

      见下例:

      import java.util.Date;
      
      public final class ImmutableClass
      {
      
          /**
          * Integer class is immutable as it does not provide any setter to change its content
          * */
          private final Integer immutableField1;
          /**
          * String class is immutable as it also does not provide setter to change its content
          * */
          private final String immutableField2;
          /**
          * Date class is mutable as it provide setters to change various date/time parts
          * */
          private final Date mutableField;
      
          //Default private constructor will ensure no unplanned construction of class
          private ImmutableClass(Integer fld1, String fld2, Date date)
          {
              this.immutableField1 = fld1;
              this.immutableField2 = fld2;
              this.mutableField = new Date(date.getTime());
          }
      
          //Factory method to store object creation logic in single place
          public static ImmutableClass createNewInstance(Integer fld1, String fld2, Date date)
          {
              return new ImmutableClass(fld1, fld2, date);
          }
      
          //Provide no setter methods
      
          /**
          * Integer class is immutable so we can return the instance variable as it is
          * */
          public Integer getImmutableField1() {
              return immutableField1;
          }
      
          /**
          * String class is also immutable so we can return the instance variable as it is
          * */
          public String getImmutableField2() {
              return immutableField2;
          }
      
          /**
          * Date class is mutable so we need a little care here.
          * We should not return the reference of original instance variable.
          * Instead a new Date object, with content copied to it, should be returned.
          * */
          public Date getMutableField() {
              return new Date(mutableField.getTime());
          }
      
          @Override
          public String toString() {
              return immutableField1 +" - "+ immutableField2 +" - "+ mutableField;
          }
      }
      

      更多详情: http://www.javainterviewpoint.com/make-class-immutable-java/

      【讨论】:

        【解决方案8】:

        这样做的唯一方法。 创建一个自定义 DateClass 并覆盖 set** 方法,并且不为此方法提供任何功能。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2018-10-19
          • 2012-05-16
          • 2021-08-23
          • 1970-01-01
          • 1970-01-01
          • 2017-05-13
          相关资源
          最近更新 更多