如何在Groovy中从静态超类方法引用子类

艾略特·纳尔逊(Elliot Nelson)

我正在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

我想我在这里问的是:当我在对象上调用静态方法,并且该静态方法在对象的超类中定义时,有没有办法获取对象的实际类型?

希蒙·斯蒂芬尼克

静态方法不是在对象上下文中定义的,而是在类上下文中定义的。thisGroovy静态方法的存在可能会使您感到困惑然而,这只是一个语法糖,最终取代了this.nameAnimal.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方法-在DogandCat类中覆盖静态方法我可以假设您的真实方法可能比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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

如何在超类静态函数中从子类引用静态参数?

如何在超类的静态方法中检索子类的静态属性

您如何在Java中从超类调用子类方法?

我如何在超类方法中使用子类的静态字段

如何在超类的静态函数中获取调用子类的名称?

Javascript ES6:如何从超类中定义的静态方法中检索调用子类

如何在Kotlin中获得对Java超类的静态类的引用

如何在Objective-C中引用超类的子类视图?

确定用于调用子类中的方法的超类引用

超类引用无法在Java中调用子类方法

Java:如何在抽象类中引用子类的静态变量?

如何在Java中从其超类方法停止/退出子类方法执行

如何在超类方法中使用子类参数?

如何在继承的方法中引用子类?

如何在超类中定义一个方法,使其与任何子类兼容?

如何在超类方法中访问子类属性,而不将其传递给

如何从修饰的子类方法中调用重写的超类方法

如何在类静态成员中引用静态变量?

如何让子类扩展python中超类的方法

如何访问子类中超类的受保护方法?

如何在Groovy中声明方法引用?

如何覆盖子类中基类的静态抽象方法?

Java:如何使用超类的静态方法从其创建子类的实例

对超类对象的子类引用

如何使超类方法返回子类的实例

如何使子类自动调用超类方法

如何在Java中对非静态方法进行静态引用?

如何在类中访问静态方法

当子类对象存储在超类数组中时,如何调用子类方法?