Using Groovy MetaClass to Dynamically Add Methods and Properties to Classes and Objects
This article demonstrates how Groovy's MetaClass can be used at runtime to add object methods, static methods, and properties to classes, and includes a complete Spock unit‑test example that verifies dynamic behavior with detailed code snippets and console output.
During a recent study session the author discovered that, contrary to the common belief that Java and Groovy classes have fixed members, Groovy's groovy.lang.MetaClass allows dynamic addition of methods and properties at runtime.
Adding Object Methods
The simplified syntax is object.metaClass.object_method = { closure } . Example:
def funTester = new FunTester()
funTester.metaClass.test = {
logger.info("我是测试方法:{}", "test")
}
funTester.test()Console output shows the custom method being invoked.
Adding Static Methods
The syntax for static methods is object.metaClass.static.object_method = { closure } . Example:
def funTester = new FunTester()
funTester.metaClass.test = {
logger.info("我是测试方法:{}", "static.test")
}
funTester.test()Console output confirms the static‑style method execution.
Adding Properties
Properties can be added with funTester.metaClass.setProperty("name", "FunTester") and retrieved via funTester.getProperty("name") :
funTester.metaClass.setProperty("name","FunTester")
logger.info(funTester.getProperty("name"))The class FunTester itself is an empty placeholder used for these demonstrations.
Spock Unit Test
The article then presents a Spock test suite that exercises the dynamic additions. The test class imports logging, defines shared instances, and contains three feature methods:
"测试动态添加对象方法" verifies that a dynamically added object method returns 12 .
"测试动态添加静态方法" checks a dynamically added static method on the class.
"测试动态添加获取属性" attempts to set and get a dynamic property, demonstrating both a passing and a failing case.
package com.funtest.spock
import org.apache.logging.log4j.LogManager
import org.apache.logging.log4j.Logger
import spock.lang.Shared
class Mop extends spock.lang.Specification {
@Shared
private static final Logger logger = LogManager.getLogger(Mop.class);
def setup() { logger.info("测试方法开始了") }
def cleanup() { logger.info("测试方法结束了") }
def setupSpec() { /* dynamic metaClass manipulations */ }
def cleanupSpec() { logger.info("测试类[${getClass().getName()}]结束了") }
@Shared def fun = new Mop()
def "测试动态添加对象方法"() {
given:
fun.metaClass.ob_method = { return 12 }
expect:
12 == fun.ob_method()
}
def "测试动态添加静态方法"() {
given:
Mop.metaClass.static.clas_method = { return 12 }
expect:
12 == Mop.clas_method()
}
def "测试动态添加获取属性"() {
expect:
fun.setName(name) == tt
where:
name | tt
"FunTester"| "FunTester"
"Have Fun" | "Have Fun"
}
}The test run produces console logs, shows one failing assertion for the second data row, and summarizes the overall test results (5 tests, 2 failures).
FunTester
10k followers, 1k articles | completely useless
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.