JDK11升级至JDK17:Groovy中Date的getAt方法参数类型变更问题
问题背景
将Groovy应用从JDK11(Groovy 2.4.15)升级到JDK17(Groovy 4.0.20)后,原有通过Groovy下标访问器调用Date.getAt(int)获取日期天数的代码报错:
原有代码:
public static def getDayFromDate(Date date) { return date[Calendar.DAY_OF_MONTH] }
报错信息:
groovy.lang.MissingMethodException: No signature of method: java.util.Date.getAt() is applicable for argument types: (Integer) values: [5]
发现Groovy4.0中Date.getAt()仅支持String参数(如date["date"]可获取天数),但希望继续使用Calendar枚举值的写法,并了解参数类型变更的原因。
原因分析
Groovy归属变更与API适配调整:Groovy从Codehaus迁移到Apache后,Groovy4.x版本对JDK废弃API的处理逻辑做了大幅调整。
java.util.Date在JDK11+中已有大量方法被标记为废弃,Groovy2.x为Date添加的getAt(int)扩展方法是基于Calendar的封装,但Groovy4.x为贴合JDK的废弃导向,移除了该int参数版本的扩展,转而提供更直观的String参数版本(如"year"、"month"、"date"等对应Calendar字段)。JDK17模块系统与反射限制:JDK17的模块系统对反射操作的限制更严格,Groovy2.x中
Date.getAt(int)可能依赖反射调用Calendar的相关方法,而JDK17的权限控制导致这种实现方式不再可行,因此Groovy4.x选择了更安全的String参数方案。
替代方案
方案1:复用Calendar枚举值的传统写法
直接使用Calendar标准API,兼容性好:
public static def getDayFromDate(Date date) { def calendar = Calendar.getInstance() calendar.time = date return calendar.get(Calendar.DAY_OF_MONTH) }
注意:
Calendar不是线程安全的,多线程环境建议每次新建实例或使用ThreadLocal<Calendar>保证线程安全。
方案2:自定义扩展方法恢复原有写法
通过Groovy扩展机制为Date重新添加getAt(int)方法,继续使用Calendar枚举值:
- 创建扩展类:
package com.yourcompany.extensions import java.util.Calendar import java.util.Date class DateExtension { static Object getAt(Date date, int calendarField) { def calendar = Calendar.getInstance() calendar.time = date return calendar.get(calendarField) } }
- 注册扩展:在
src/main/resources/META-INF/services/org.codehaus.groovy.runtime.ExtensionModule文件中添加:
moduleName=custom-date-extension moduleVersion=1.0 extensionClasses=com.yourcompany.extensions.DateExtension
完成后即可继续使用date[Calendar.DAY_OF_MONTH]的写法。
方案3:迁移到Java 8+日期API(推荐)
JDK17完全支持Java 8引入的java.time API,该API线程安全且设计更合理,建议替换旧的Date和Calendar:
import java.time.ZoneId public static def getDayFromDate(Date date) { return date.toInstant() .atZone(ZoneId.systemDefault()) .toLocalDate() .getDayOfMonth() }
内容的提问来源于stack exchange,提问作者Reed M

