A Python module to access Java classes as Python classes using JNI.
—
PyJNIus provides essential utilities for managing Java object lifecycle, type casting, class discovery, and exception handling. These functions enable fine-grained control over Java interoperability and proper resource management.
Function for casting Java objects between different Java class types.
def cast(target_class: type, obj: object) -> object:
"""
Cast Java object to target Java class type.
Args:
target_class: Target Java class (created with autoclass or manual definition)
obj: Java object to cast
Returns:
Java object cast to target type
Raises:
JavaException: If cast is invalid (ClassCastException)
"""Usage Examples:
from jnius import autoclass, cast
# Get Java classes
Object = autoclass('java.lang.Object')
String = autoclass('java.lang.String')
ArrayList = autoclass('java.util.ArrayList')
List = autoclass('java.util.List')
# Create objects
text = String('Hello')
list_obj = ArrayList()
# Cast String to Object (upcast - always safe)
obj = cast(Object, text)
print(obj.toString()) # Hello
# Cast ArrayList to List interface (upcast - always safe)
list_interface = cast(List, list_obj)
list_interface.add('item')
# Cast Object back to String (downcast - requires runtime check)
try:
original_text = cast(String, obj)
print(original_text.length()) # 5
except JavaException as e:
print(f"Cast failed: {e}")
# Invalid cast example
try:
invalid = cast(ArrayList, text) # Will fail
except JavaException as e:
print(f"Invalid cast: {e}") # ClassCastExceptionFunction for finding and loading Java classes by name.
def find_javaclass(class_name: str) -> object:
"""
Find and load Java class by fully qualified name.
Args:
class_name: Fully qualified Java class name (dot notation)
Returns:
Java Class object, or None if not found
Note:
Used internally by autoclass and reflection system.
"""Usage Examples:
from jnius import find_javaclass
# Find standard Java classes
string_class = find_javaclass('java.lang.String')
if string_class:
print(f"Found class: {string_class.getName()}") # java.lang.String
# Check if custom class exists
custom_class = find_javaclass('com.example.MyClass')
if custom_class is None:
print("Custom class not found on classpath")
# Find array classes
string_array_class = find_javaclass('[Ljava.lang.String;')
int_array_class = find_javaclass('[I')
# Find nested classes (use $ separator)
nested_class = find_javaclass('java.util.Map$Entry')Function for managing JVM thread attachment and detachment.
def detach() -> None:
"""
Detach current thread from JVM.
Required in multithreaded applications where threads that call Java
methods need to be properly detached before thread termination.
Note:
Automatically called on Android for all threads.
Should be called manually in desktop applications when using
Java from background threads.
"""Usage Examples:
import threading
from jnius import autoclass, detach
def background_task():
try:
# Use Java classes in background thread
String = autoclass('java.lang.String')
result = String('background task')
print(result.toUpperCase())
finally:
# Always detach thread before termination
detach()
# Start background thread
thread = threading.Thread(target=background_task)
thread.start()
thread.join()
# Example with thread pool
from concurrent.futures import ThreadPoolExecutor
def process_data(data):
try:
ArrayList = autoclass('java.util.ArrayList')
list_obj = ArrayList()
list_obj.add(data)
return list_obj.size()
finally:
detach()
with ThreadPoolExecutor(max_workers=4) as executor:
futures = [executor.submit(process_data, f"data_{i}") for i in range(10)]
results = [future.result() for future in futures]Exception wrapper for Java exceptions with Python integration.
class JavaException(Exception):
"""
Python exception wrapper for Java exceptions.
Attributes:
classname: Fully qualified name of Java exception class
innermessage: Original Java exception message
stacktrace: Java exception stack trace
"""
classname: str # Java exception class name
innermessage: str # Original Java exception message
stacktrace: str # Java exception stack traceUsage Examples:
from jnius import autoclass, JavaException
try:
# This will throw java.lang.StringIndexOutOfBoundsException
String = autoclass('java.lang.String')
text = String('hello')
char = text.charAt(10) # Index out of bounds
except JavaException as e:
print(f"Java exception: {e.classname}")
print(f"Message: {e.innermessage}")
print(f"Stack trace: {e.stacktrace}")
# Check specific exception types
if 'StringIndexOutOfBoundsException' in e.classname:
print("String index was out of bounds")
# Re-raise as Python exception
raise IndexError(f"Java string index error: {e.innermessage}")
# Exception handling with multiple Java operations
def safe_java_operation():
try:
ArrayList = autoclass('java.util.ArrayList')
HashMap = autoclass('java.util.HashMap')
# Multiple operations that could fail
list_obj = ArrayList()
map_obj = HashMap()
# This could throw various exceptions
list_obj.add(None) # Might throw NullPointerException in some contexts
value = map_obj.get('nonexistent') # Returns null, safe
return list_obj, map_obj
except JavaException as e:
print(f"Java operation failed: {e.classname} - {e.innermessage}")
if e.stacktrace:
print(f"Stack trace: {e.stacktrace}")
return None, NoneConstants and utilities for Java object memory management.
HASHCODE_MAX: int # Maximum Java hash code value (2^31 - 1)Usage in Custom Classes:
from jnius import PythonJavaClass, MetaJavaClass, HASHCODE_MAX
class MyPythonJavaClass(PythonJavaClass, metaclass=MetaJavaClass):
__javaclass__ = 'com/example/MyClass'
def custom_hash(self):
# Use HASHCODE_MAX for consistent Java-compatible hashing
return id(self) % HASHCODE_MAXLocal Reference Management: PyJNIus automatically manages JNI local references, but for long-running operations with many Java objects, consider:
# Process objects in batches to avoid local reference exhaustion
def process_large_dataset(data_items):
batch_size = 100
results = []
for i in range(0, len(data_items), batch_size):
batch = data_items[i:i+batch_size]
# Process batch
batch_results = []
for item in batch:
java_obj = create_java_object(item)
result = java_obj.process()
batch_results.append(str(result)) # Convert to Python type
results.extend(batch_results)
# Optional: Force garbage collection between batches
import gc
gc.collect()
return resultsResource Management Best Practices:
from jnius import autoclass
# Use context managers for closeable resources
def read_file_safely(filename):
FileInputStream = autoclass('java.io.FileInputStream')
try:
stream = FileInputStream(filename)
# Use the stream
data = []
while True:
byte = stream.read()
if byte == -1: # EOF
break
data.append(byte)
return bytes(data)
finally:
if 'stream' in locals():
stream.close()
# Or leverage protocol_map for automatic cleanup
def read_file_with_context_manager(filename):
FileInputStream = autoclass('java.io.FileInputStream')
with FileInputStream(filename) as stream:
# Stream automatically closed when exiting context
data = []
while True:
byte = stream.read()
if byte == -1:
break
data.append(byte)
return bytes(data)Install with Tessl CLI
npx tessl i tessl/pypi-pyjnius