如何重写DefaultGroovyMethods的toSet方法以保留集合元素顺序
问题原因
Groovy中DefaultGroovyMethods提供的JDK类扩展方法优先级高于通过metaClass动态添加的方法,尤其是Collection这类核心JDK类的自带扩展,默认情况下元编程覆写会被Groovy的方法调用路由逻辑跳过,所以你写的闭包不会被触发。
可行解决方案
方案1:注册自定义Groovy扩展模块(全局生效,最稳定)
这是官方推荐的覆写默认扩展方法的方式,自定义扩展的优先级高于内置的DefaultGroovyMethods:
- 新建自定义扩展类,放到你的项目包路径下:
package com.yourpackage.util class CustomCollectionExtensions { static <T> Set<T> toSet(Collection<T> self) { return new LinkedHashSet<T>(self) } }
- 在
src/main/resources/META-INF/services目录下创建名为org.codehaus.groovy.runtime.ExtensionModule的配置文件,内容如下:
moduleName=custom-collection-extensions moduleVersion=1.0 extensionClasses=com.yourpackage.util.CustomCollectionExtensions
重启项目后全局的Collection#toSet都会走你自定义的LinkedHashSet实现。
方案2:启动时批量注册元类方法(适配Grails启动逻辑)
如果不想做扩展模块,可以在Grails的grails-app/init/BootStrap.groovy的init方法中添加如下代码,主动给所有常用集合实现类覆写方法:
def init = { servletContext -> def toSetImpl = {-> Collection self = (Collection) delegate return new LinkedHashSet(self) } // 给Collection接口和常用实现类分别注册 [Collection, ArrayList, LinkedList, Vector, HashSet, LinkedHashSet].each { clazz -> GroovySystem.metaClassRegistry.getMetaClass(clazz).toSet = toSetImpl } }
方案3:封装工具方法(无侵入,最稳妥)
如果不需要全局覆写,只是部分业务需要保留顺序,直接封装独立工具方法即可,不会影响其他依赖默认toSet逻辑的功能:
class CollectionUtils { static <T> Set<T> toLinkedSet(Collection<T> collection) { return collection == null ? Collections.emptySet() : new LinkedHashSet<>(collection) } }
内容的提问来源于stack exchange,提问作者ilPittiz
相关产品推荐
相关产品推荐

