Jython is an implementation of Python 2.7 written in 100% Pure Java, providing seamless integration with the Java platform and ecosystem.
—
Jython provides seamless integration between Java and Python, allowing automatic type conversion, method invocation, and exception handling across both platforms.
Automatic conversion from Java objects to Python objects.
public final class Py {
// Generic conversion
public static PyObject java2py(Object o);
// Specific conversions
public static PyString java2py(String s);
public static PyInteger java2py(int i);
public static PyInteger java2py(long l);
public static PyFloat java2py(float f);
public static PyFloat java2py(double d);
public static PyBoolean java2py(boolean b);
public static PyObject java2py(Object o);
}Convert Python objects back to Java types.
public abstract class PyObject {
// Generic conversion
public Object __tojava__(Class<?> c);
// Common conversions available on specific types
public String toString();
public boolean equals(Object o);
public int hashCode();
}
// Specific conversion methods on respective classes
public class PyString {
public String getString();
public String asString();
}
public class PyInteger {
public int getValue();
public int asInt();
public long asLong();
public double asDouble();
}
public class PyFloat {
public double getValue();
public double asDouble();
public int asInt();
}
public class PyBoolean {
public boolean getBooleanValue();
public boolean __bool__();
}// Java to Python
String javaStr = "Hello, World!";
PyString pyStr = Py.java2py(javaStr);
int javaInt = 42;
PyInteger pyInt = Py.java2py(javaInt);
double javaDouble = 3.14159;
PyFloat pyFloat = Py.java2py(javaDouble);
boolean javaBool = true;
PyBoolean pyBool = Py.java2py(javaBool);
// Python to Java
String backToJava = pyStr.getString();
int backToInt = pyInt.asInt();
double backToDouble = pyFloat.asDouble();
boolean backToBool = pyBool.getBooleanValue();// Java List to Python List
List<String> javaList = Arrays.asList("apple", "banana", "cherry");
PyList pyList = new PyList();
for (String item : javaList) {
pyList.append(Py.java2py(item));
}
// Python List to Java List
List<String> backToJavaList = new ArrayList<>();
for (int i = 0; i < pyList.__len__(); i++) {
PyObject item = pyList.__getitem__(i);
backToJavaList.add(item.toString());
}// Java Map to Python Dictionary
Map<String, Object> javaMap = new HashMap<>();
javaMap.put("name", "Alice");
javaMap.put("age", 30);
javaMap.put("active", true);
PyDictionary pyDict = new PyDictionary();
for (Map.Entry<String, Object> entry : javaMap.entrySet()) {
PyString key = Py.java2py(entry.getKey());
PyObject value = Py.java2py(entry.getValue());
pyDict.put(key, value);
}
// Python Dictionary to Java Map
Map<String, Object> backToJavaMap = new HashMap<>();
PyList keys = pyDict.keys();
for (int i = 0; i < keys.__len__(); i++) {
PyObject key = keys.__getitem__(i);
PyObject value = pyDict.get(key);
backToJavaMap.put(key.toString(), value.__tojava__(Object.class));
}PythonInterpreter interp = new PythonInterpreter();
// Make Java objects available to Python
ArrayList<String> javaList = new ArrayList<>();
javaList.add("item1");
javaList.add("item2");
interp.set("java_list", javaList);
// Access Java methods from Python
interp.exec("""
print("Java list size:", java_list.size())
java_list.add("item3")
for item in java_list:
print("Item:", item)
""");
interp.close();PythonInterpreter interp = new PythonInterpreter();
// Import Java classes
interp.exec("""
from java.util import ArrayList, HashMap
from java.lang import System
# Create Java objects
list = ArrayList()
list.add("Hello")
list.add("World")
map = HashMap()
map.put("key1", "value1")
map.put("key2", "value2")
# Call Java static methods
System.out.println("From Python: " + str(list))
""");
interp.close();Jython provides proxy classes for seamless Java-Python integration.
public class JavaProxyList extends PySequenceList implements List {
public JavaProxyList(List list);
// Implements both Python sequence protocol and Java List interface
public boolean add(Object o);
public void add(int index, Object element);
public boolean remove(Object o);
public Object remove(int index);
public Object get(int index);
public Object set(int index, Object element);
public int size();
public boolean isEmpty();
public void clear();
}public class JavaProxyMap extends AbstractDict implements Map {
public JavaProxyMap(Map map);
// Implements both Python mapping protocol and Java Map interface
public Object put(Object key, Object value);
public Object get(Object key);
public Object remove(Object key);
public boolean containsKey(Object key);
public Set keySet();
public Collection values();
public Set entrySet();
public int size();
public boolean isEmpty();
public void clear();
}// Wrap Java collections for Python use
List<String> javaList = new ArrayList<>();
JavaProxyList proxyList = new JavaProxyList(javaList);
PythonInterpreter interp = new PythonInterpreter();
interp.set("proxy_list", proxyList);
interp.exec("""
# Use as Python list
proxy_list.append("Hello")
proxy_list.append("World")
print("Length:", len(proxy_list))
for item in proxy_list:
print(item)
""");
// Changes are reflected in original Java list
System.out.println("Java list: " + javaList);
interp.close();PythonInterpreter interp = new PythonInterpreter();
// Define Python function
interp.exec("""
def greet(name, age=None):
if age:
return f"Hello, {name}! You are {age} years old."
else:
return f"Hello, {name}!"
""");
// Get function object
PyObject greetFunc = interp.get("greet");
// Call with positional arguments
PyObject result1 = greetFunc.__call__(new PyObject[]{Py.newString("Alice")});
System.out.println(result1); // Hello, Alice!
// Call with keyword arguments
PyObject result2 = greetFunc.__call__(
new PyObject[]{Py.newString("Bob"), Py.newInteger(30)},
new String[]{"name", "age"}
);
System.out.println(result2); // Hello, Bob! You are 30 years old.
interp.close();PythonInterpreter interp = new PythonInterpreter();
// Define Java class with methods
class Calculator {
public static int add(int a, int b) {
return a + b;
}
public int multiply(int a, int b) {
return a * b;
}
}
// Make Java class available
interp.set("Calculator", Calculator.class);
interp.set("calc_instance", new Calculator());
interp.exec("""
# Call static method
result1 = Calculator.add(5, 3)
print("5 + 3 =", result1)
# Call instance method
result2 = calc_instance.multiply(4, 7)
print("4 * 7 =", result2)
""");
interp.close();PythonInterpreter interp = new PythonInterpreter();
try {
interp.exec("raise ValueError('Something went wrong')");
} catch (PyException e) {
// Check exception type
if (e.match(Py.ValueError)) {
System.out.println("Caught ValueError: " + e.value);
}
// Get exception details
PyObject excType = e.type;
PyObject excValue = e.value;
PyTraceback traceback = e.traceback;
System.out.println("Exception type: " + excType);
System.out.println("Exception value: " + excValue);
}
interp.close();class RiskyClass {
public static void riskyMethod() throws IOException {
throw new IOException("File not found");
}
}
PythonInterpreter interp = new PythonInterpreter();
interp.set("RiskyClass", RiskyClass.class);
interp.exec("""
try:
RiskyClass.riskyMethod()
except Exception as e:
print("Caught Java exception:", type(e).__name__)
print("Message:", str(e))
""");
interp.close();Jython provides adapter mechanisms for custom type conversion:
public interface ClassicPyObjectAdapter {
public Object adapt(PyObject o);
public PyObject adapt(Object o);
public boolean canAdapt(Object o);
}
public interface ExtensiblePyObjectAdapter {
public Object adapt(PyObject o);
public PyObject adapt(Object o);
public boolean canAdapt(Object o);
}Java beans can be accessed using Python attribute syntax:
class Person {
private String name;
private int age;
public String getName() { return name; }
public void setName(String name) { this.name = name; }
public int getAge() { return age; }
public void setAge(int age) { this.age = age; }
}
PythonInterpreter interp = new PythonInterpreter();
interp.set("person", new Person());
interp.exec("""
# Access bean properties as Python attributes
person.name = "Alice"
person.age = 30
print(f"Name: {person.name}")
print(f"Age: {person.age}")
""");
interp.close();Java iterables work seamlessly with Python iteration:
List<String> items = Arrays.asList("apple", "banana", "cherry");
PythonInterpreter interp = new PythonInterpreter();
interp.set("items", items);
interp.exec("""
# Iterate over Java list using Python syntax
for item in items:
print("Item:", item)
# Use with Python list comprehensions
upper_items = [item.upper() for item in items]
print("Upper case:", upper_items)
""");
interp.close();// Avoid repeated conversions
// Bad:
for (int i = 0; i < 1000; i++) {
PyObject pyInt = Py.java2py(i);
// use pyInt
}
// Better:
PyObject[] pyInts = new PyObject[1000];
for (int i = 0; i < 1000; i++) {
pyInts[i] = Py.java2py(i);
}// Reuse common objects
PyString separator = Py.newString(",");
PyList result = Py.newList();
for (String item : items) {
if (result.__len__() > 0) {
result.append(separator);
}
result.append(Py.java2py(item));
}Type conversion operations are generally thread-safe, but be careful with:
// Thread-safe pattern
public class ThreadSafeConverter {
private static final ThreadLocal<PythonInterpreter> INTERPRETER =
ThreadLocal.withInitial(() -> PythonInterpreter.threadLocalStateInterpreter(null));
public PyObject convert(Object javaObj) {
return Py.java2py(javaObj);
}
public Object convert(PyObject pyObj, Class<?> targetClass) {
return pyObj.__tojava__(targetClass);
}
}Install with Tessl CLI
npx tessl i tessl/maven-org-python--jython-standalone