How to dynamically add all methods of a class into another class

僤鯓⒐⒋嵵緔 提交于 2021-01-28 14:22:51

问题


I have a global shared library on Jenkins implicitly loaded on all pipelines, then my Jenkinsfile is like that:

new com.company.Pipeline()()

And then the shared library has on directory src/com/company some files, below the Pipeline.groovy class:

package com.company  

import static Utils.*

def call() {
  // some stuff here...
}

The problem is, this way I have to static declare all methods, thus I lose the context and cannot access jenkins' methods easly without the Pipeline class' instance. As you can see here they passing this to the method mvn.

Thinking of avoid this I was wondering about dynamically add all methods as closures by calling Utils.install this instead of using import static Utils.*, then my Utils.groovy is something like that:

package com.company

private Utils() {}

static def install(def instance) {
  def utils = new Utils()
  // Some extra check needed here I know, but it is not the problem now
  for (def method in (utils.metaClass.methods*.name as Set) - (instance.metaClass.methods*.name as Set)) {
    def closure = utils.&"$method"
    closure.delegate = instance
    instance.metaClass."$method" = closure
  }
}

def someMethod() {
  // here I want to use sh(), tool(), and other stuff freely.
}

But it raises an GStringImpl cannot be cast to String error, I believe .& do not work with variables, how can I convert a method into closure having the method name on a variable? I have the MetaMethod mostly being a CachedMethod instance, if it were possible to turn it a ClosureMetaMethod instance maybe the problem can be solved, but whenever I search for method to closure conversion for groovy I just found the .& solution!

If I use instance.metaClass.someMethod = utils.&someMethod it do work, but I want it to be dinamic as I add new methods without needing to worry about sharing it.


回答1:


There is a way to do it dynamically. Notation utils.&someMethod returns a MethodClosure object that can be simply instantiated with its constructor:

MethodClosure(Object owner, String method)

Consider following example:

class Utils {
    def foo() {
        println "Hello, Foo!"
    }
    def bar() {
        println "Hello, Bar!"
    }
}

class Consumer {
}

def instance = new Consumer()
def utils = new Utils()

(utils.metaClass.methods*.name - instance.metaClass.methods*.name).each { method ->
    def closure = new MethodClosure(utils, method)
    closure.delegate = instance
    instance.metaClass."$method" = closure
}

instance.foo() // Prints "Hello, Foo!"
instance.bar() // Prints "Hello, Bar!"

In this example I use def closure = new MethodClosure(utils, method) to get object method reference and then add this method to instance object. I hope it helps.



来源:https://stackoverflow.com/questions/45600613/how-to-dynamically-add-all-methods-of-a-class-into-another-class

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!