Skip to content

RubyCallingJava

Patrick Plenefisch edited this page Sep 22, 2021 · 1 revision
Clone this wiki locally

This page describes the logic used for dispatching from Ruby to Java, including how methods are selected and how users can modify the process from Ruby.

Background

JRuby allows calling Java classes from Ruby by wrapping those classes with Ruby-friendly class/module structures we call "proxies". The proxies look and feel like normal Ruby classes or modules, and can be modified, monkey-patched, and re-opened. All methods of a given name in the Java class are bound to a single Ruby method of the same name.

For Java methods with no overloads, the dispatching process is simply to coerce passed arguments to the types of the one target method. For Java methods with overloads, the process is more complicated, and a "best match" is selected before proceeding with coercion.

JRuby 1.1.5-1.3.1 Dispatch Logic

For releases from JRuby 1.1.5 through JRuby 1.3.1, the method selection logic worked like this:

For each callable...

  • try to find a 100% exact match using class.getJavaClass (only one allowed per type)
  • try to find an assignable&primitivable match using JavaClass.assignable and type-specific checks for numerics
  • try to find an assignable&duckable match using JavaClass.assignable and JavaUtil.isDuckTypeConvertible
If any of the above finds results, we calculate an "exactness" score and the remaining searches do not fire. The most exact match in a given step is chosen as the target.

Exactness is calculated based on how many of the target callable's parameter types exactly matches that returned from class.getJavaClass. In the case of a "100% exact match" search, obviously the successful result is the most exact automatically.

Selecting a Target Method

Coercing Values

Overriding Coercion or Selection Behavior

Forcibly Casting Values

Troubleshooting

Concrete Extensions: Initialization

Sometimes, such as with JavaFX, calling Java requires passing a constructable Java sub-class along. If you are attempting to pass a subclass of a java class, concrete extension is the path to look into.

This path was significantly revamped to use the same reification process as normal Ruby classes in 9.3, via https://github.com/jruby/jruby/pull/6422 . See the particular comment https://github.com/jruby/jruby/pull/6422#issuecomment-748414730 for a near-final version of how initialization happens. Notably, when Java calls class.newInstance(...), the reified java proxy is called first, which calls (by default, configurable with JavaClassConfig) initialize in a split-super style way. This runs ruby `initialize` code up until the `super`, at which point the ruby method state is saved, and control is passed back down to the proxy constructor, which calls super, then resumes execution of super.