Java 思考:在 JRuby 中确定 java.util.regex.Pattern 的 class

Java Reflection: Determining class of java.util.regex.Pattern in JRuby

在 Java 11 下处理遗留 JRuby 应用程序 (1.6.8) 运行,我在测试用例中发现了一个特殊错误:"require 'java'; puts java::util::regex::Pattern.class" 错误 ArgumentError: wrong number of arguments (0 for 1)。对于其他内置 JRE 类,这似乎工作正常(见下文)。

这导致 JRuby 本身有时会失败,在 https://github.com/jruby/jruby/blob/1.6.8/lib/ruby/site_ruby/shared/builtin/javasupport/core_ext/object.rb#L10 中的这一行:

if self.class.superclass.instance_method(:method_added) != method(:java_package_method_added)

为了让应用程序正常工作,我不得不在 JRuby 源代码中注释掉该行。

我对造成这种情况的原因或如何正确解决它感到困惑。 Google 显示了几个人在尝试加载特定应用程序时遇到该错误的实例,但没有人弄清楚原因(所有解决方案都说“尝试不同版本的应用程序”)。

为什么JRuby不能执行.class?它期待什么参数?

下面的完整跟踪:

$ java -version
java version "11.0.1" 2018-10-16 LTS
Java(TM) SE Runtime Environment 18.9 (build 11.0.1+13-LTS)
Java HotSpot(TM) 64-Bit Server VM 18.9 (build 11.0.1+13-LTS, mixed mode)

$ ./jruby -v
jruby 1.6.8 (ruby-1.8.7-p357) (2012-09-18 1772b40) (Java HotSpot(TM) 64-Bit Server VM 11.0.1) [linux-amd64-java]

$ ./jruby -e "require 'java'; puts java::util::regex::Matcher.class"
WARNING: An illegal reflective access operation has occurred
...
Class

$ ./jruby -e "require 'java'; puts java::lang::String.class"
WARNING: An illegal reflective access operation has occurred
...
Class

$ ./jruby -e "require 'java'; puts java::util::regex::Pattern.class"
WARNING: An illegal reflective access operation has occurred
....
ArgumentError: wrong number of arguments (0 for 1)
  (root) at -e:1

Java 行为

Java本身好像没有问题:

jshell> int.class
 ==> int

jshell> String.class
 ==> class java.lang.String

jshell> Pattern.class
 ==> class java.util.regex.Pattern

jshell> Pattern.class.getClass()
 ==> class java.lang.Class

jshell> String.class.getClass()
 ==> class java.lang.Class

部分解决方案?

我可以通过注释掉上面引用的 if 语句来修复 JRuby 中的这个错误。但是,在这个遗留应用程序中,JRuby 位于编译后的 jar 中,因此我无法编辑其源代码。有没有办法在不更改二进制 jar 的情况下向 JRuby 添加猴子补丁来修复失败的 if 语句?

获取 JRuby

JRuby 1.6.8 可在 http://central.maven.org/maven2/org/jruby/jruby-dist/1.6.8/

获得

认为 jruby 正在尝试读取模式 class:

的成员之一
private static int getClass(int c) {
    return sun.text.Normalizer.getCombiningClass(c);
}

因为它看起来有点像 getter 但有那个额外的参数。

您可以通过创建自己的 class 来确认:

class MyClass {
    private static int getClass(int c) {
        return 4;
    }
}

然后尝试在 JRuby 中从中获取 class。

它适用于 java 8,因为这个方法是非静态的,就像 Holger 所说的那样,jruby 似乎更喜欢使用现有的静态方法,即使它们与签名不匹配,并检查特殊的“.class" 属性 最后完成。 (如 java Something.class 不是 属性,而只是一个关键字)

最好的选择是为匹配调用的方法(需要 0 个参数)以及 public 方法设置更高的优先级。另外 getters 应该只匹配 属性 的名字,只有当它们真的是 getters - 所以他们没有任何参数。

我无法下载此版本的 jruby,因此无法测试:https://www.jruby.org/files/downloads/1.6.8/index.html