Java 是否有可能延长Joda的期末考试时间? 我真的很喜欢JoDA时间,但我遇到了一些我认为是个问题。我想扩展一些类,特别是DateTime、LocalDate和LocalDateTime。但它们被标记为“最终”

Java 是否有可能延长Joda的期末考试时间? 我真的很喜欢JoDA时间,但我遇到了一些我认为是个问题。我想扩展一些类,特别是DateTime、LocalDate和LocalDateTime。但它们被标记为“最终”,java,jodatime,Java,Jodatime,我发现了一个非常古老的线程,其中解释为确保类保持不变的一种方法 我在这里还发现了一个线程,讨论了是否需要将Java类标记为final以确保不变性 无论如何,我发现不能扩展这些类是一个主要的限制。除了下载源文件并修改它们之外,还有什么办法可以创建这些类的扩展版本吗 编辑-讨论: 扩展类的能力是面向对象编程中最强大、最有用的概念之一。这总是有用的。一个类的作者不可能100%确信他/她的超级复制器类在扩展到涵盖任何人都无法预见的用例时,对某些程序员来说不会更有用 Joda时间类被标记为“final”的

我发现了一个非常古老的线程,其中解释为确保类保持不变的一种方法

我在这里还发现了一个线程,讨论了是否需要将Java类标记为final以确保不变性

无论如何,我发现不能扩展这些类是一个主要的限制。除了下载源文件并修改它们之外,还有什么办法可以创建这些类的扩展版本吗

编辑-讨论:

扩展类的能力是面向对象编程中最强大、最有用的概念之一。这总是有用的。一个类的作者不可能100%确信他/她的超级复制器类在扩展到涵盖任何人都无法预见的用例时,对某些程序员来说不会更有用

Joda时间类被标记为“final”的明显原因是为了确保某人不可能创建可变的扩展类,并将其用于依赖于Joda时间对象的不可变的现有程序。因此,在某种程度上,将这些类标记为“final”是因为缺少一种Java语言机制,该机制允许将类标记为“不可变”,因此它们可以被扩展,但前提是扩展类也标记为“不可变”

因此,考虑到Java中缺少“不可变”关键字,我可以理解Joda Time的作者希望避免这种情况

以下解决方案可行吗?我们可以有一个从LocalDateNonFinal派生LocalDate的结构吗?LocalDate是一个标记为“final”的空类。所有功能都在LocalDateNonFinal中

因此,如果您确实想要扩展LocalDate类,并且只打算在您自己的程序中使用扩展的类,那么您可以改为扩展LocalDateNonFinal,并将其称为MyLocalDate。这不会使其他模块暴露于您可能犯的错误,因为它们仍然需要LocalDate,并且不接受LocalDateNonFinal或您的MyLocalDate

这可以与试图教育想要扩展这些类的程序员相结合,如果他们意外地创建了可变版本,并且仍然将其视为不可变的,则警告他们可能出现的问题。并指出这些扩展类将不可用于其他需要常规(“最终”)类的模块


另外,我会在几天后发布我的变通解决方案,当我完全确定的时候。到目前为止,我对其中两个答案投了赞成票——谢谢你的评论和建议。我目前倾向于使用类似于包装器的解决方案,正如Dmitry Zaitsev所建议的那样。

如果您真的需要扩展类,您可以从这里获得源代码:

分叉它们,将它们标记为非最终的,并扩展它们


否则您无法扩展最终类。

如果您确实需要扩展类,您可以从这里获得源代码:

分叉它们,将它们标记为非最终的,并扩展它们


否则您就不能扩展final类。

不可能扩展任何标记为final的类,并且分叉这些类是不实际的。除您自己的代码外,代码中的对象将期望看到Joda类,并将验证它们得到的是什么,您将无法传递您自己的版本。所以使用forking的最佳情况是,您将有一组对象供自己使用,您必须将它们转换为Joda或java8,以便与其他代码一起使用。此外,您的分叉版本不会从将来对原始类所做的任何修复中获益,除非您继续将修复复制到您自己的版本中。另一个问题可能是Joda类和您自己的版本之间的比较可能不可传递,结果可能取决于调用它们的对象


您可以使用所有静态方法创建一个实用程序类,这些静态方法将接受一个Joda对象,执行您想要的任何额外功能并返回一个Joda对象。这类似于在apache commons或java.lang.Math中找到的StringUtil类。这样,您就避免了forking的维护,并且可以直接与库或框架代码一起使用。

不可能扩展任何标记为final的内容,而且forking这些类也不实用。除您自己的代码外,代码中的对象将期望看到Joda类,并将验证它们得到的是什么,您将无法传递您自己的版本。所以使用forking的最佳情况是,您将有一组对象供自己使用,您必须将它们转换为Joda或java8,以便与其他代码一起使用。此外,您的分叉版本不会从将来对原始类所做的任何修复中获益,除非您继续将修复复制到您自己的版本中。另一个问题可能是Joda类和您自己的版本之间的比较可能不可传递,结果可能取决于调用它们的对象


您可以使用所有静态方法创建一个实用程序类,这些静态方法将接受一个Joda对象,执行您想要的任何额外功能并返回一个Joda对象。这类似于在apache commons或java.lang.Math中找到的StringUtil类。这样,您就避免了forking的维护,并且可以直接与库或框架代码一起使用。

您可以将最终类包装到自己的类中,提供您想要的任何操作,并提供“view”方法,该方法将返回原始的Joda time对象。就像这样:

public class MyJodaExtension {

    private final DateTime dateTime;

    public MyJodaExtension(DateTime dateTime) {
        this.dateTime = dateTime;
    }

    public boolean myOperation() {
        return false;  // or whatever you need
    }

    public DateTime asDateTime() {
        return dateTime;
    }

}
通过这种方法,您甚至可以使
MyJodaExtension
可变并提供不同的
package com.Merlinia.MCopier_Main;

import org.joda.time.DateTime;


/**
 * This common base class is used to provide a (mutable) wrapper for the (immutable) Joda-Time
 * DateTime and LocalDateTime classes. It is used as the base class (super class, in Java
 * terminology) for the DateTimeLocal and DateTimeUtc classes. This provides a somewhat kludgy way
 * of extending the DateTime and LocalDateTime classes, since they can't be directly extended
 * because they are marked "final".
 *
 * The only service provided by this class is that it contains a field which can contain the .Net
 * DateTime "ticks" value that was used to create this object via MCopier deserialization. This is
 * then used by MCopier serialization to provide an identical result if the object is round-tripped
 * from .Net to Java and back again, although only if the associated DateTime or LocalDateTime
 * object has not been updated. (If the DateTime or LocalDateTime object is updated then this field
 * is set to Long.MIN_VALUE, and is no longer considered valid.) Sending an identical result back to
 * .Net simplifies the testing program, as well as avoiding unnecessary loss of precision. (Joda-
 * Time is only precise to the nearest millisecond. .Net DateTime ticks can, in theory, be precise
 * to the nearest 100 nanoseconds.)
 */
public abstract class DateTimeCommon {

   // See here: http://stackoverflow.com/questions/3706306/c-sharp-datetime-ticks-equivalent-in-java
   private static final long CTicksAtEpoch = 621355968000000000L;
   private static final long CTicksPerMillisecond = 10000;


   private long _dotNetDateTimeTicks;  // Long.MIN_VALUE means not valid


   // Constructor for new object, not due to MCopier deserialization
   public DateTimeCommon() {
      _dotNetDateTimeTicks = Long.MIN_VALUE;
   }

   // Copy constructor
   public DateTimeCommon(DateTimeCommon copyFrom) {
      _dotNetDateTimeTicks = copyFrom._dotNetDateTimeTicks;
   }

   // Constructor used by MCopier deserialization
   public DateTimeCommon(long dotNetDateTimeTicks) {
      _dotNetDateTimeTicks = dotNetDateTimeTicks;
   }


   protected void indicateDotNetTicksNotValid() {
      _dotNetDateTimeTicks = Long.MIN_VALUE;
   }


   // Method used by MCopier deserialization to compute the number of milliseconds in Java notation
   // that corresponds to a long int containing a .Net DateTime value in "ticks". But note that
   // although Java millis are normally always based on the UTC time zone, that the millis returned
   // by this method in the case of a .Net DateTimeLocal value are not UTC-based; they are
   // independent of time zone and represent a different instant in time for different time zones.
   // See also here:
   // http://stackoverflow.com/questions/3706306/c-sharp-datetime-ticks-equivalent-in-java
   protected static long convertTicksToMillis(long dotNetDateTimeTicks) {

      return
         (dotNetDateTimeTicks - CTicksAtEpoch + CTicksPerMillisecond / 2) / CTicksPerMillisecond;
   }


   // Method used by MCopier serialization
   protected long getAsDotNetTicks(DateTime jodaDateTime) {

      if (_dotNetDateTimeTicks != Long.MIN_VALUE) {
         return _dotNetDateTimeTicks;
      }
      return (jodaDateTime.getMillis() * CTicksPerMillisecond) + CTicksAtEpoch;
   }
}
package com.Merlinia.MCopier_Main;

import org.joda.time.DateTimeZone;
import org.joda.time.LocalDateTime;


/**
 * This class provides a (mutable) wrapper for the (immutable) Joda-Time LocalDateTime class. See
 * comments on the DateTimeCommon base class.
 *
 * All manipulation of this object should consist of a call to getJodaLocalDateTime() followed by
 * manipulation of the LocalDateTime object, producing a new (immutable) LocalDateTime object,
 * followed by a call to setJodaLocalDateTime().
 *
 * When doing MCopier serialization and deserialization from/to .Net DateTime "ticks" we do
 * something a bit sneaky: We pretend that the ticks represent a UTC time, and we pretend that the
 * associated Joda-Time LocalDateTime object also represents UTC time. Both of these pretences are
 * (normally) false, but the end result is that it works. See also here:
 * http://stackoverflow.com/questions/11665404/simplest-way-to-get-local-milliseconds-in-a-time-zone-with-joda-time
 */
public class DateTimeLocal extends DateTimeCommon {

   private LocalDateTime _jodaLocalDateTime;


   // Constructor for new object, not due to MCopier deserialization
   public DateTimeLocal(LocalDateTime jodaLocalDateTime) {
      super();
      _jodaLocalDateTime = jodaLocalDateTime;
   }

   // Copy constructor
   public DateTimeLocal(DateTimeLocal copyFrom) {
      super(copyFrom);
      _jodaLocalDateTime = copyFrom._jodaLocalDateTime;
   }

   // Constructor used by MCopier deserialization
   public DateTimeLocal(long dotNetDateTimeTicks) {
      super(dotNetDateTimeTicks);
      _jodaLocalDateTime = new LocalDateTime(
                        DateTimeCommon.convertTicksToMillis(dotNetDateTimeTicks), DateTimeZone.UTC);
   }


   public LocalDateTime getJodaLocalDateTime() {
      return _jodaLocalDateTime;
   }

   public void setJodaLocalDateTime(LocalDateTime jodaLocalDateTime) {
      _jodaLocalDateTime = jodaLocalDateTime;
      super.indicateDotNetTicksNotValid();
   }


   // Method used by MCopier serialization
   public long getAsDotNetTicks() {
      return super.getAsDotNetTicks(_jodaLocalDateTime.toDateTime(DateTimeZone.UTC));
   }
}