关于Java多态的一些疑问适用于Calendar和GregoriaCalendar案例

时间:2016-08-25 14:58:16

标签: java polymorphism gregorian-calendar java.util.calendar

我有以下疑问。

我的代码中有:

Calendar today = Calendar.getInstance();

其中today变量是日历的一个实例,因此我无法在其上使用 isLeapYear()方法。

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

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

boolean bisestile = today.isLeapYear(currentYear);

我怀疑是:为什么这么说?我正在将 Calendar.getInstance()返回的相同结果instange转换为 GregorianCalendar

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

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

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

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

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

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

3 个答案:

答案 0 :(得分:2)

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

如果您真正想要的是GregorianCalendar,请明确声明变量。

GregorianCalendar cal = new GregorianCalendar();

答案 1 :(得分:0)

Answer by ControlAltDel是正确的。

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

因此,一般规则是使用满足您需求的最高级别界面

示例:集合

例如,在Java Collections中,使用符合您需求的Collection界面。

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

仅当您的代码需要更具体的界面提供的其他方法时才更具体。在这里,我们使用ListCollection的子接口承诺使用其他方法。

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

仅在绝对必要的情况下才将ArrayList用作ArrayList,前提是该类提供了您需要的某些方法,而这些方法未包含在其更通用的界面中。

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

稍后我们可能决定将ArrayList替换为LinkedList。这样做不会破坏任何期望ListCollection的代码,但会破坏任何特定期望ArrayList的调用代码。

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

Calendar&amp; GregorianCalendar

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

Calendar cal = new GregorianCalendar() ;

但如果代码绝对需要GregorianCalendar的特殊功能,则将对象跟踪为GregorianCalendar,如Answer by ControlAltDel所示。

GregorianCalendar cal = new GregorianCalendar() ;

你可以把它混合起来,在内部使用更具体的类型,在外部使用更通用的类型。如果您只需要在自己的类中使用这些特殊的GregorianCalendar功能,但希望将日历公开给其他类,则可以在返回时将日历对象声明为类的私有成员GregorianCalendar {来自getter方法的{1}}。

Calendar

P.S。 … private GregorianCalendar gregCal = new GregorianCalendar() ; … public Calendar getCalendar() { return this.gregCal ; // Automatically upcast. Appears to any calling code to be a `Calendar`. } &amp; java.util.Calendar是现在由Java 8及更高版本中内置的java.time类取代的麻烦的旧遗留日期时间类的一部分。避免使用这些旧课程。

答案 2 :(得分: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的主要设计者也采用了这个view to be as concrete as possible

  

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

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