Skip to content

Commit 96e9269

Browse files
docs: DOC-1076: Add guidance for accessing Java inner classes in Groovy and Python (#7964)
1 parent f396c7b commit 96e9269

12 files changed

Lines changed: 79 additions & 10 deletions

docs/groovy/how-to-guides/java-classes.md

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,29 @@ source = emptyTable(10).update(
6464
sourceMeta = source.meta()
6565
```
6666

67+
## Nested (inner) classes
68+
69+
Java allows users to define classes inside other classes. In Groovy, you access these nested (inner) classes using dot notation — `OuterClass.InnerClass` — which is standard Java syntax.
70+
71+
```groovy order=null test-set=inner-classes-groovy
72+
import io.deephaven.engine.table.impl.util.SyncTableFilter
73+
74+
// Access the Builder inner class of SyncTableFilter using dot notation
75+
def builder = new SyncTableFilter.Builder()
76+
```
77+
78+
You can also import the inner class directly to use it without the outer class qualifier:
79+
80+
```groovy order=null test-set=inner-classes-groovy
81+
import io.deephaven.engine.table.impl.util.SyncTableFilter.Builder
82+
83+
// Use Builder directly after importing
84+
def builder2 = new Builder()
85+
```
86+
87+
> [!NOTE]
88+
> If you load a class dynamically with `Class.forName()`, use `$` as the separator — `OuterClass$InnerClass`. This is the JVM's internal binary class name format. In Groovy source code, always use dot notation.
89+
6790
## Groovy's Java interoperability
6891

6992
One of Groovy's greatest strengths is its seamless interoperability with Java. In Groovy query strings, you can:
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"file":"how-to-guides/java-classes.md","objects":{}}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"file":"how-to-guides/java-classes.md","objects":{}}

docs/python/how-to-guides/use-jpy.md

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ import jpy
4949

5050
_JURL = jpy.get_type("java.net.URL")
5151

52-
# the print function is overloaded to print the classpath of the underlying Java type
52+
# the print function is overloaded to print the fully qualified class name of the underlying Java type
5353
print(_JURL)
5454
# the class gets recognized as a Python type, confirming that this is a Python object
5555
print(type(_JURL))
@@ -64,6 +64,48 @@ help(_JURL)
6464

6565
Check the [Javadoc for `java.net.URL`](https://docs.oracle.com/en/java/javase/17/docs/api/java.base/java/net/URL.html) to confirm that all of its methods are represented by the Python wrapping.
6666

67+
### Access nested (inner) classes
68+
69+
In Java, inner classes are accessed with dot notation: `OuterClass.InnerClass`. However, [`jpy.get_type`](/core/pydoc/code/jpy.html#jpy.get_type) expects the JVM's internal binary class name, which uses `$` as the separator between outer and inner class names. Using dot notation will fail.
70+
71+
```python should-fail
72+
import jpy
73+
74+
# ❌ Dot notation does not work for inner classes
75+
_JMapEntry = jpy.get_type("java.util.Map.Entry")
76+
```
77+
78+
Use `$` instead:
79+
80+
```python test-set=inner-classes
81+
import jpy
82+
83+
# ✅ Use $ to separate the outer and inner class names
84+
_JMapEntry = jpy.get_type("java.util.Map$Entry")
85+
```
86+
87+
The pattern is: `jpy.get_type("fully.qualified.OuterClass$InnerClass")`. This applies to all levels of nesting — `OuterClass$InnerClass$DeepInner` for classes nested two levels deep.
88+
89+
Here are examples using Deephaven types:
90+
91+
```python test-set=inner-classes
92+
# Builder inner class of SyncTableFilter
93+
_JSyncTableFilterBuilder = jpy.get_type(
94+
"io.deephaven.engine.table.impl.util.SyncTableFilter$Builder"
95+
)
96+
97+
# Builder inner classes of LeaderTableFilter
98+
_JLeaderTableFilterTableBuilder = jpy.get_type(
99+
"io.deephaven.engine.util.LeaderTableFilter$TableBuilder"
100+
)
101+
_JLeaderTableFilterPartitionedTableBuilder = jpy.get_type(
102+
"io.deephaven.engine.util.LeaderTableFilter$PartitionedTableBuilder"
103+
)
104+
```
105+
106+
> [!NOTE]
107+
> The `$`-separated name is the JVM's internal binary class name. You can find it in the Java source or Javadoc — look for `static class InnerClass` inside the outer class definition. The separator is always `$`, never `.`.
108+
67109
## Call methods on Java types
68110

69111
Python code can use Java methods directly. This means you can call any Java function from your Python program.
@@ -109,9 +151,9 @@ print(float_instance_as_string)
109151
print(type(float_instance_as_string))
110152

111153
# the compareTo method returns -1 if float is less than the argument, 0 if equal, and 1 if greater
112-
less_than_or_grearter_than = float_instance.compareTo(654.321)
113-
print(less_than_or_grearter_than)
114-
print(type(less_than_or_grearter_than))
154+
less_than_or_greater_than = float_instance.compareTo(654.321)
155+
print(less_than_or_greater_than)
156+
print(type(less_than_or_greater_than))
115157

116158
# the equals method returns True if the float is equal to the argument, and False otherwise
117159
are_floats_equal = float_instance.equals(123.456)
@@ -647,7 +689,7 @@ transformed_t = Table(_j_transformed_t)
647689
## Related documentation
648690

649691
- [Python-Java Boundary](../conceptual/python-java-boundary.md)
650-
- [Jpy Github](https://github.com/jpy-consortium/jpy)
651-
- [Jpy Javadoc](/core/javadoc/io/deephaven/jpy/package-summary.html)
692+
- [jpy GitHub](https://github.com/jpy-consortium/jpy)
693+
- [jpy documentation](https://jpy.readthedocs.io/en/latest/)
652694
- [Jpy Pydoc](/core/pydoc/code/jpy.html#module-jpy)
653695
- [Type signatures](https://docs.oracle.com/en/java/javase/17/docs/specs/jni/types.html)
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
{"file":"core/docs/how-to-guides/use-jpy.md","objects":{":log":{"type":"Log","data":"Help on class URL:\n\nclass URL(java.lang.Object)\n | Method resolution order:\n | URL\n | java.lang.Object\n | jpy.JType\n | builtins.object\n | \n | Methods defined here:\n | \n | __delattr__(self, name, /)\n | Implement delattr(self, name).\n | \n | __eq__(self, value, /)\n | Return self==value.\n | \n | __ge__(self, value, /)\n | Return self>=value.\n | \n | __getattribute__(self, name, /)\n | Return getattr(self, name).\n | \n | __gt__(self, value, /)\n | Return self>value.\n | \n | __hash__(self, /)\n | Return hash(self).\n | \n | __init__(self, /, *args, **kwargs)\n | Initialize self. See help(type(self)) for accurate signature.\n | \n | __le__(self, value, /)\n | Return self<=value.\n | \n | __lt__(self, value, /)\n | Return self<value.\n | \n | __ne__(self, value, /)\n | Return self!=value.\n | \n | __repr__(self, /)\n | Return repr(self).\n | \n | __setattr__(self, name, value, /)\n | Implement setattr(self, name, value).\n | \n | __str__(self, /)\n | Return str(self).\n | \n | ----------------------------------------------------------------------\n | Static methods defined here:\n | \n | __new__(*args, **kwargs) from builtins.type\n | Create and return a new object. See help(type) for accurate signature.\n | \n | ----------------------------------------------------------------------\n | Data and other attributes defined here:\n | \n | __jinit__ = jpy.JOverloadedMethod(class='java.net.URL', name='__jinit_...\n | \n | equals = jpy.JOverloadedMethod(class='java.net.URL', name='equals', me...\n | \n | getAuthority = jpy.JOverloadedMethod(class='java.net.URL', name='getAu...\n | \n | getClass = jpy.JOverloadedMethod(class='java.net.URL', name='getClass'...\n | \n | getContent = jpy.JOverloadedMethod(class='java.net.URL', name='getCont...\n | \n | getDefaultPort = jpy.JOverloadedMethod(class='java.net.URL', name='get...\n | \n | getFile = jpy.JOverloadedMethod(class='java.net.URL', name='getFile', ...\n | \n | getHost = jpy.JOverloadedMethod(class='java.net.URL', name='getHost', ...\n | \n | getPath = jpy.JOverloadedMethod(class='java.net.URL', name='getPath', ...\n | \n | getPort = jpy.JOverloadedMethod(class='java.net.URL', name='getPort', ...\n | \n | getProtocol = jpy.JOverloadedMethod(class='java.net.URL', name='getPro...\n | \n | getQuery = jpy.JOverloadedMethod(class='java.net.URL', name='getQuery'...\n | \n | getRef = jpy.JOverloadedMethod(class='java.net.URL', name='getRef', me...\n | \n | getUserInfo = jpy.JOverloadedMethod(class='java.net.URL', name='getUse...\n | \n | hashCode = jpy.JOverloadedMethod(class='java.net.URL', name='hashCode'...\n | \n | jclass = java.lang.Class(objectRef=0xffff929dfd72)\n | \n | jclassname = 'java.net.URL'\n | \n | notify = jpy.JOverloadedMethod(class='java.net.URL', name='notify', me...\n | \n | notifyAll = jpy.JOverloadedMethod(class='java.net.URL', name='notifyAl...\n | \n | of = jpy.JOverloadedMethod(class='java.net.URL', name='of', methodCoun...\n | \n | openConnection = jpy.JOverloadedMethod(class='java.net.URL', name='ope...\n | \n | openStream = jpy.JOverloadedMethod(class='java.net.URL', name='openStr...\n | \n | sameFile = jpy.JOverloadedMethod(class='java.net.URL', name='sameFile'...\n | \n | setURLStreamHandlerFactory = jpy.JOverloadedMethod(class='java.net.URL...\n | \n | toExternalForm = jpy.JOverloadedMethod(class='java.net.URL', name='toE...\n | \n | toString = jpy.JOverloadedMethod(class='java.net.URL', name='toString'...\n | \n | toURI = jpy.JOverloadedMethod(class='java.net.URL', name='toURI', meth...\n | \n | wait = jpy.JOverloadedMethod(class='java.net.URL', name='wait', method...\n\n"}}}
1+
{"file":"how-to-guides/use-jpy.md","objects":{":log":{"type":"Log","data":"Help on class URL:\n\nclass URL(java.lang.Object)\n | Method resolution order:\n | URL\n | java.lang.Object\n | jpy.JType\n | builtins.object\n |\n | Methods defined here:\n |\n | __delattr__(self, name, /)\n | Implement delattr(self, name).\n |\n | __eq__(self, value, /)\n | Return self==value.\n |\n | __ge__(self, value, /)\n | Return self>=value.\n |\n | __getattribute__(self, name, /)\n | Return getattr(self, name).\n |\n | __gt__(self, value, /)\n | Return self>value.\n |\n | __hash__(self, /)\n | Return hash(self).\n |\n | __init__(self, /, *args, **kwargs)\n | Initialize self. See help(type(self)) for accurate signature.\n |\n | __le__(self, value, /)\n | Return self<=value.\n |\n | __lt__(self, value, /)\n | Return self<value.\n |\n | __ne__(self, value, /)\n | Return self!=value.\n |\n | __repr__(self, /)\n | Return repr(self).\n |\n | __setattr__(self, name, value, /)\n | Implement setattr(self, name, value).\n |\n | __str__(self, /)\n | Return str(self).\n |\n | ----------------------------------------------------------------------\n | Static methods defined here:\n |\n | __new__(*args, **kwargs)\n | Create and return a new object. See help(type) for accurate signature.\n |\n | ----------------------------------------------------------------------\n | Data and other attributes defined here:\n |\n | __jinit__ = jpy.JOverloadedMethod(class='java.net.URL', name='__jinit_...\n |\n | equals = jpy.JOverloadedMethod(class='java.net.URL', name='equals', me...\n |\n | getAuthority = jpy.JOverloadedMethod(class='java.net.URL', name='getAu...\n |\n | getClass = jpy.JOverloadedMethod(class='java.net.URL', name='getClass'...\n |\n | getContent = jpy.JOverloadedMethod(class='java.net.URL', name='getCont...\n |\n | getDefaultPort = jpy.JOverloadedMethod(class='java.net.URL', name='get...\n |\n | getFile = jpy.JOverloadedMethod(class='java.net.URL', name='getFile', ...\n |\n | getHost = jpy.JOverloadedMethod(class='java.net.URL', name='getHost', ...\n |\n | getPath = jpy.JOverloadedMethod(class='java.net.URL', name='getPath', ...\n |\n | getPort = jpy.JOverloadedMethod(class='java.net.URL', name='getPort', ...\n |\n | getProtocol = jpy.JOverloadedMethod(class='java.net.URL', name='getPro...\n |\n | getQuery = jpy.JOverloadedMethod(class='java.net.URL', name='getQuery'...\n |\n | getRef = jpy.JOverloadedMethod(class='java.net.URL', name='getRef', me...\n |\n | getUserInfo = jpy.JOverloadedMethod(class='java.net.URL', name='getUse...\n |\n | hashCode = jpy.JOverloadedMethod(class='java.net.URL', name='hashCode'...\n |\n | jclass = java.lang.Class(objectRef=0xffff8a46226a)\n |\n | jclassname = 'java.net.URL'\n |\n | notify = jpy.JOverloadedMethod(class='java.net.URL', name='notify', me...\n |\n | notifyAll = jpy.JOverloadedMethod(class='java.net.URL', name='notifyAl...\n |\n | of = jpy.JOverloadedMethod(class='java.net.URL', name='of', methodCoun...\n |\n | openConnection = jpy.JOverloadedMethod(class='java.net.URL', name='ope...\n |\n | openStream = jpy.JOverloadedMethod(class='java.net.URL', name='openStr...\n |\n | sameFile = jpy.JOverloadedMethod(class='java.net.URL', name='sameFile'...\n |\n | setURLStreamHandlerFactory = jpy.JOverloadedMethod(class='java.net.URL...\n |\n | toExternalForm = jpy.JOverloadedMethod(class='java.net.URL', name='toE...\n |\n | toString = jpy.JOverloadedMethod(class='java.net.URL', name='toString'...\n |\n | toURI = jpy.JOverloadedMethod(class='java.net.URL', name='toURI', meth...\n |\n | wait = jpy.JOverloadedMethod(class='java.net.URL', name='wait', method...\n\n"}}}

docs/python/snapshots/37b75d3dba344f37f4f57fff3cbca2f2.json

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"file":"how-to-guides/use-jpy.md","objects":{":log":{"type":"Log","data":"123.456\n<class 'str'>\n-1\n<class 'int'>\nFalse\n<class 'bool'>\n"}}}

docs/python/snapshots/40ff718d134d91423fc93e11d41f184f.json

Lines changed: 0 additions & 1 deletion
This file was deleted.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"file":"how-to-guides/use-jpy.md","objects":{}}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"file":"how-to-guides/use-jpy.md","objects":{}}

0 commit comments

Comments
 (0)