我正在Groovy中尝试做的简化版本:
class Animal {
static def echo() {
println this.name // ie "class.name"
}
}
class Dog extends Animal {
}
class Cat extends Animal {
}
Dog.echo()
Cat.echo()
// Output:
// => Animal
// => Animal
//
// What I want:
// => Dog
// => Cat
我想我在这里问的是:当我在对象上调用静态方法,并且该静态方法在对象的超类中定义时,有没有办法获取对象的实际类型?
静态方法不是在对象上下文中定义的,而是在类上下文中定义的。this
Groovy静态方法的存在可能会使您感到困惑。然而,这只是一个语法糖,最终取代了this.name
用Animal.class.name
。
如果在Animal
启用了静态编译的情况下从示例中编译该类,您将看到它编译为以下等效的Java(反编译.class文件后的结果):
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.GroovyObject;
import groovy.lang.MetaClass;
import org.codehaus.groovy.runtime.DefaultGroovyMethods;
public class Animal implements GroovyObject {
public Animal() {
MetaClass var1 = this.$getStaticMetaClass();
this.metaClass = var1;
}
public static Object echo() {
DefaultGroovyMethods.println(Animal.class, Animal.class.getName());
return null;
}
}
您可以在echo
方法中看到以下行:
DefaultGroovyMethods.println(Animal.class, Animal.class.getName());
直接对Animal
类名称进行操作。因此,从echo
方法的角度来看,扩展多少类并不重要。只要这些类调用该类中echo
定义的方法Animal
,您始终将看到Animal
打印出来的结果。
甚至还有更多。如果使用以下编译器配置脚本:
config.groovy
withConfig(configuration) {
ast(groovy.transform.CompileStatic)
ast(groovy.transform.TypeChecked)
}
然后使用此配置选项和以下命令来编译脚本(将其称为script.groovy):
groovyc --configscript=config.groovy script.groovy
那么在反编译.class文件后,您将看到类似以下内容:
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
import groovy.lang.Binding;
import org.codehaus.groovy.runtime.InvokerHelper;
public class script extends groovy.lang.Script {
public script() {
}
public script(Binding context) {
super(context);
}
public static void main(String... args) {
InvokerHelper.runScript(script.class, args);
}
public Object run() {
Animal.echo();
return Animal.echo();
}
}
您可以看到,即使您已调用,Dog.echo()
并且Cat.echo()
在Groovy脚本中,编译器也将这些调用替换为两次Animal.echo()
调用。发生这种情况是因为在任何其他子类上调用此静态方法没有任何区别。
有一种获得预期输出的echo
方法-在Dog
andCat
类中覆盖静态方法。我可以假设您的真实方法可能比echo
上面显示的示例方法做更多的事情,因此您可能需要echo
从父类调用super方法。但是...有两个问题:(1)您不能super.echo()
在静态上下文中使用,以及(2)它不能解决问题,因为父方法仍在Animal
类上下文中运行。
要解决此类问题,您可能需要模仿一种称为double dispatch的技术。简而言之-当我们在被调用的方法中没有关于调用方的信息时,让我们允许调用方通过方法调用传递此信息。考虑以下示例:
import groovy.transform.CompileStatic
@CompileStatic
class Animal {
// This is a replacement for the previous echo() method - this one knows the animal type from a parameter
protected static void echo(Class<? extends Animal> clazz) {
println clazz.name
}
static void echo() {
echo(Animal)
}
}
@CompileStatic
class Dog extends Animal {
static void echo() {
echo(Dog)
}
}
@CompileStatic
class Cat extends Animal {
static void echo() {
echo(Cat)
}
}
Animal.echo()
Dog.echo()
Cat.echo()
这听起来像样板解决方案-它要求echo
在每个子类中实现方法。但是,它将echo
逻辑封装在需要Class<? extends Animal>
参数的方法中,因此我们可以让每个子类介绍其具体的子类型。当然,这不是一个完美的解决方案。它要求echo
在每个子类中实现方法,但是没有其他替代方法。另一个问题是,这不会阻止您拨打电话Dog.echo(Animal)
,这将导致与通话相同的效果Animal.echo()
。这种类似于双重分派的方法更像是介绍方法的简化版本,该echo
方法使用通用的静态echo
方法实现以简化操作。
我不知道这种方法是否可以解决您的问题,但也许它将帮助您找到最终的解决方案。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句