1

我有以下疑问。

在我的代码中,我有:

Calendar today = Calendar.getInstance();

今天变量是Calendar的一个实例,所以我不能在它上面使用isLeapYear()方法。

这样做我可以执行此方法:

GregorianCalendar today = (GregorianCalendar) Calendar.getInstance();
int currentYear = today.get(Calendar.YEAR);

boolean bisestile = today.isLeapYear(currentYear);

我的疑问是:为什么?我将Calendar.getInstance()返回的相同结果实例转换为GregorianCalendar

在这里阅读:http: //tutorials.jenkov.com/java-date-time/java-util-calendar.html

在我看来,java.util.Calendar类是抽象的,所以我无法实例化它,所以我认为Calendar.getInstance()自动返回一个定义了先前isLeapYear()方法的GregorianCalendar对象。

但是,如果对象被定义为简单的Calendar而不是GregorianCalendar,我将无法使用它。

我知道多态性,但在这种特定情况下究竟是如何工作的?

我认为将GregorianCalendar对象的引用(由Calendar.getInstance()返回,是真的吗?)放入Calendar(我可以这样做,因为Calendar是超类型)我只能访问为此定义的方法子集抽象类,而不是为具体类型定义的所有方法。

这个推理是正确的还是我错过了什么?

4

3 回答 3

2

这就是多态性Calendar提供了一个抽象框架,像 GregorianCalendar 这样的子类提供了实现。在其他情况下,请了解Calendar.getInstance()可能会返回(例如)中文或希伯来日历,具体取决于地点和系统设置。

如果你真正想要的是一个GregorianCalendar明确的,那么声明变量。

GregorianCalendar cal = new GregorianCalendar();
于 2016-08-25T15:05:24.233 回答
0

ControlAltDel的答案是正确的。

面向对象编程的好处之一是防止脆弱的软件在进行一些更改后崩溃。实现这一点的方法之一是我们定义一个具有一个或多个实现的接口。当其他代码引用该接口时,我们可以在不破坏调用代码的情况下切换实现。

所以一般规则是使用满足您需求的最高级别的接口

示例:集合

例如,在Java Collections中,使用Collection满足您需求的接口。

Collection<String> col = new ArrayList<>() ;

仅当您的代码需要更具体的接口提供的其他方法时,才能更具体。在这里,我们使用List,它的子接口Collection承诺额外的方法。

List<String> list = new ArrayList<>() ;

仅在绝对必要时才使用ArrayListArrayList仅当该类提供了您需要的某些方法,而这些方法未包含在其更通用的接口中。

ArrayList<String> list = new ArrayList<>() ;

稍后我们可能会决定将其替换ArrayListLinkedList. 这样做不会破坏任何期望 a 的代码,ListCollection破坏任何期望 a 的调用代码ArrayList

List<String> list = new LinkedList<>();

Calendar&GregorianCalendar

同样,您应该Calendar尽可能使用,而不是GregorianCalendar.

Calendar cal = new GregorianCalendar() ;

但是,如果代码绝对需要的特殊功能,GregorianCalendar则将对象跟踪为 a ,如ControlAltDelGregorianCalendar的答案中所示。

GregorianCalendar cal = new GregorianCalendar() ;

您可以混合使用,在内部使用更具体的类型,在外部使用更通用的类型。如果您只需要GregorianCalendar自己的类中的那些特殊功能,但想将日历公开给其他类,则可以在从 getter 方法GregorianCalendar返回时将日历对象声明为类的私有成员。Calendar

…
private GregorianCalendar gregCal = new GregorianCalendar() ;
…
public Calendar getCalendar() {
    return this.gregCal ;  // Automatically upcast. Appears to any calling code to be a `Calendar`.
}

PS java.util.Calendar&.GregorianCalendar是麻烦的旧日期时间类的一部分,现在已被 Java 8 及更高版本中内置的 java.time 类所取代。避免这些旧课程。

于 2016-08-25T22:32:08.580 回答
0

如果您在泰国,那么您的代码

GregorianCalendar today = (GregorianCalendar) Calendar.getInstance();

可能会通过抛出一个失败,ClassCastException因为Calendar.getInstance()可以产生泰国佛教日历作为该国家的默认值。Calendar.getInstance()如果您真的在泰国,这个用例表明要使用的强烈警告。然后,当您要解释诸如calendar.get(Calendar.YEAR)(可能会产生诸如 2543 之类的年份值)之类的表达式时,您就无法做出基于公历的假设。

结论:在处理日期和时间时,最好不要使用通用接口,而是尽可能具体。我假设你只对公历感兴趣,所以请使用:

GregorianCalendar today = new GregorianCalendar();

顺便说一句,Java-8 中新 java.time-package 的主要设计者也采用了这种观点,以尽可能具体

大多数应用程序应该将方法签名、字段和变量声明为 LocalDate,而不是这个接口。

实际上,这意味着:您应该避免使用 等类型TemporalAccessorChronoLocalDate或者在旧世界中:避免使用诸如此类java.util.Calendar的类型,而使用诸如GregorianCalendar. 在日期和时间的世界中,多态并不是一个好主意。具体类型太不同了(有时以一种非常微妙的方式)。

于 2016-08-29T11:31:44.680 回答