【问题标题】:How to call a simple getter method in Groovy?如何在 Groovy 中调用一个简单的 getter 方法?
【发布时间】:2015-07-05 00:43:54
【问题描述】:

我不敢相信我不得不问这个问题,这真的很令人困惑。这个问题的目的是找出为什么和/或找到一种更简单的方法(比反思)来获得正确的结果。

背景故事

我通过URLClassLoader 从目录和 jar 文件中加载了一些类文件,我想转储所有类名及其声明的方法。无法初始化这些类(请参阅false),因为如果在这些类中运行任何代码,则可能由于缺少某些依赖项而引发异常。我在尝试只输出类名时遇到了这个问题。

问题

我错过了什么,我怎样才能绕过 Groovy 的魔法,而只是简单地在对象(java.lang.Class 类型)上调用一个方法(称为 getName)而不进行反射?也请有人指出规范说明为什么会这样工作。
这是我的迷你测试(见下文)和我的 cmets 的输出:

.name // magically initializes class X and calls X.get("name")
name and Y

.getName() // tries to reload class Y in another ClassLoader and initialize it
X and thrown SHOULD NOT INIT

["name"] // this is just plain magic! What a Terrible Failure :)
name and thrown SHOULD NOT INIT

reflection // obviously works, becase it's really explicit
X and Y

测试工具和测试用例

将测试闭包更改为在参数类型上显式 (Class<?> c ->) 没有任何区别。

new File("X.java").write('''
    public class X {
        public static String get(String key) {
            return key;
        }
    }
    ''');
new File("Y.java").write('''
    public class Y {
        static {
            if (true) // needed to prevent compile error
                throw new UnsupportedOperationException("SHOULD NOT INIT");
        }
    }
    ''');
print 'javac X.java Y.java'.execute().err.text;


def test = { String title, Closure nameOf ->
    URL url = new File(".").toURI().toURL();
    // need a new ClassLoader each time because it remembers
    // if a class has already thrown ExceptionInInitializerError
    ClassLoader loader = java.net.URLClassLoader.newInstance(url);
    // false means not to initialize the class.
    // To get the name of the class there's no need to init
    // as shown in the reflection test.
    // Even fields and methds can be read without initializing,
    // it's essentially just parsing the .class file.
    Class x = Class.forName("X", false, loader);
    Class y = Class.forName("Y", false, loader);

    println()
    println title
    try {
        print nameOf(x)
    } catch (Throwable ex) {
        print "thrown " + ex.cause?.message
    }
    print " and "
    try {
        print nameOf(y)
    } catch (Throwable ex) {
        print "thrown " + ex.cause?.message
    }
    println()
}

test '.name', { c -> c.name; }
test '.getName()', { c -> c.getName(); }
test '["name"]', { c -> c["name"] }
test 'reflection', { c -> java.lang.Class.class.getDeclaredMethod("getName").invoke(c); }

【问题讨论】:

    标签: java methods reflection groovy gradle


    【解决方案1】:

    对于invokedynamic端口,我可以给出一个明确的答案:绕过一个JVM错误,其中JVM调用了一个类的静态方法,根本没有调用clinit。

    至于另一部分......这将是提交https://github.com/apache/incubator-groovy/commit/0653ddc15ec0215f2141159a71c1b12d8d800dbe#diff-59caa62540f88da51c8c91c6656315d5 不确定塞德里克为什么这样做。假设 JVM 可以正常工作,则不需要这样做......

    【讨论】:

    • 你能更清楚“invokedynamic”和“other”吗?我是低级 JVM 的新手。
    • 另外你知道是否有一种方法可以通过反射而不是反射来完成我所做的事情?
    • 至于invokedynamic... 在JVM 上进行方法调用,您需要知道接收者和参数基类型以及方法名称。如果您想要一种不同的逻辑,而不是在编译器时已经知道所有这些类型,例如因为您是一种动态语言,那么您不能使用这些标准调用指令。它周围有 3 个:反射、运行时类生成、invokedynamic。 Groovy 使用了所有这些。反射和invokedynamic最终也会在运行时生成类......
    • 但是invokedynamic允许你编写一个假的方法调用,然后提供你自己的缓存和方法选择逻辑。虽然要为 invokedynamic 或运行时生成的类生成信息,但您通常使用反射
    • 所以实际上没有办法绕过反射。方法调用本身可以在没有反射的情况下完成,就像调用动态和运行时类生成一样(尽管在 Groovy 中第一次调用类生成仍然使用反射)
    猜你喜欢
    • 2013-12-15
    • 1970-01-01
    • 2014-03-17
    • 1970-01-01
    • 2012-02-03
    • 2018-02-22
    • 1970-01-01
    • 1970-01-01
    • 2018-05-21
    相关资源
    最近更新 更多