ONLamp.com    
 Published on ONLamp.com (http://www.onlamp.com/)
 See this if you're having trouble printing code examples


Jython Essentials

Jython Tips for Python Programmers

by Noel Rappin, co-author of Jython Essentials
04/11/2002

In my first article about Jython, I discussed the features of the Python scripting language and its Jython implementation that would be of greatest interest to Java programmers. Jython is a complete implementation of the Python programming language written in 100 percent pure Java, and allows easy access to Java libraries.

However, if you are already a Python programmer, then you know about the features of the Python programming language. For somebody already familiar with Python, the greatest attraction of using Jython is the ability to use any of the wide range of existing Java libraries and tools while being able to still use Python as the programming language.

Jython does its best to shield a Python programmer from the details of Java, but you'll still need to know some of those details to use Java objects and libraries most effectively from Jython.

This article contains 10 tips for using Java objects in Jython. I'll try not to assume much prior knowledge of Java, but space limitations prevent me from defining all Java terms in detail. Also, in this article when I need to differentiate the standard C implementation of Python, I'll refer to it as CPython.

1. Importing Java Packages

A Java package is a collection of Java classes which are typically stored in the same directory, one class to a file. Just like Python modules, Java packages must be imported to Jython before use. In order to be imported, a Java package must be in either the Java classpath or the Python PYTHONPATH.

Java packages are imported as though they were Python modules, using the ordinary import statement. You use the fully qualified name of the module for import. To import the java.lang and java.util packages, you would type:

import java.lang
import java.util

Unlike CPython, in Jython you don't have to explicitly specify sub-packages. If you type the line:

import java

in Jython, both java.lang and java.util (and all the other sub-packages of java) are all available. This also works for standard Python modules -- in Jython, if you import os, then the os.path module is automatically imported.

Often, Jython programmers leave the full name of the Java package in the code, to emphasize the creation of Java objects. However, if you find that repetitive you can use the as clause to shorten the package name, commonly done like this:

import java.lang as lang
import java.util as util

It's recommended that you not use the from version import statement:

from java import *

in production Jython code, because of the overhead of importing all of the standard libraries, and other potential problems. In general, it's a good idea to stick to import for Java packages.

2. Creating Java Objects

Once imported, Java objects are created using the same syntax as ordinary Python objects.

x = java.lang.String()
z = java.util.HashMap()

Internally, Jython calls the appropriate Java constructor for the class given the number and type of arguments (see Tip 6 below for details) -- so the first line above calls the no-argument constructor for java.lang.String, but the line:

x = java.lang.String("Hi")

calls the one-argument constructor that expects a String. In other words, Jython is managing Java type information, so that you don't have to.

When creating Java objects (and only when creating Java objects), you can pass keyword arguments to the Jython constructor that are not part of the type signature of any Java constructor for that object. For example:

win = javax.swing.JFrame(size=(200,200))

For each keyword argument x, Jython looks for a matching public method of the class setX, and calls that method automatically. If there is no such set method, Jython raises an error.

Related Reading

Jython Essentials
By Samuele Pedroni, Noel Rappin

3. Using Objects in Python Statements

Once created, Java objects can be used in Jython statements, and will nearly always behave as though they were native Python objects (some restrictions are listed below).

Java objects can be used as the test in an if or while statement. The following Java object values are equivalent to Python false -- java.lang.Boolean FALSE, Boolean false, empty instances of java.util.Vector or java.util.Hashtable, and any empty class implementing the interfaces java.util.Dictionary, java.util.List, java.util.Map, or java.util.Collection. Note that unlike CPython, empty instances of Java strings are equivalent to Python true (empty Python strings are still false).

Any item of type java.util.Vector, java.util.Enumeration, java.util.List, or java.util.Iterator can be used as the list expression in a Jython for statement. In addition, you can use standard Python index notation (such as x[3]) for any subclass or implementer of java.util.Vector, java.util.Dictionary, java.util.List, or java.util.Map. However, slice access (such as x[3:5]) is not supported on Java objects.

Any subclass of java.lang.Exception can be raised and caught from within Jython using the normal Python try and raise statements.

4. Accessing Methods and Fields

Normally from Jython you can call any Java method or field designated as public in the Java class. You can get at private methods by changing Jython's start-up options.

Jython also allows you to avoid the ubiquitous Java get/set methods, allowing your program to have a more Pythonic style. When Jython sees a line of code such as:

x = javaObj.attribute

a Java call of the method javaObj.getAttribute(), if such a method exists, is automatically generated. Similarly, an assignment to javaObj.attribute is converted to a call to javaObj.setAttribute.

5. Creating Java Arrays

Frequently, you will need to call a Java method that requires a Java array as an argument. Under normal circumstances, Jython will automatically convert a Python list or tuple to an array of the appropriate type when needed. This assumes that each item in the list or tuple can be converted to the appropriate type -- if not, Jython will raise a runtime error. So the Jython list [1, 2, 3] can be converted to a Java int[], long[], java.lang.Integer[], or java.lang.Object[] as needed (but not, for example, to a java.lang.String[]).

However, that conversion creates a copy of the list, and in some cases that's not what you want. For example, if the method you are calling changes the list in-place, the change will not propagate back to the Jython code, since the array is only a copy of the Jython list. Jython provides the jarray module to simplify the direct creation of Java arrays when needed.

You can create blank arrays with the zeros method.

>>> import jarray
>>> jarray.zeros(5, 'i')
array([0, 0, 0, 0, 0], int)

The first argument is the length of the array, and the second is either a character representing a basic type, or a Java class name.

>>> jarray.zeros(4, java.lang.String)
array([None, None, None, None], java.lang.String)

You can create arrays directly from Python sequences, by using the array method.

>>> jarray.array([8, 9, 2, 1], 'i')
array([8, 9, 2, 1], int)

Now, the first argument is a Python sequence, and the second is still the class or type signature.

6. Overloaded Methods

In the case where the Java method is overloaded and has more than one definition, Jython will attempt to match the call to the correct method based on the number and runtime type of the arguments.

Although the details can get complex, the basic idea is simple: Jython first attempts to choose a method with the same number of arguments as the call, and if there is more than one, it chooses the method whose required type is most similar to the calling object. In general, Jython prefers methods with basic Java types over methods with Java object types.

7. Subclassing Java Objects

A Jython object can be declared as a subclass of a Java object by using the standard Python syntax:

class JythonString(java.lang.String):
    pass

Java interfaces can also be implemented in this way. Jython does not do a compile-time check to determine if all methods in an interface are actually defined -- calling an undefined method will raise a runtime exception.

Although Jython does have multiple inheritance, there is one limitation concerning Java objects. A Jython class can have at most one Java ancestor. This is true whether the ancestor is a direct parent, or an indirect parent via another Jython class.

Java subclasses behave differently when constructed. If a Jython class has a Java parent, and that parent has a no-argument constructor, that constructor is automatically called at the end of the Jython class' __init__ method, or before a Java attribute is used within that __init__ method. This is different from the normal Python behavior, where a parent class constructor is never called automatically. This is done to ensure that the Java objects are properly initialized before use.

8. Adding Attributes

Unlike ordinary Python objects, you cannot create new attributes in a Java object instance simply by assigning to it -- the instance must have been declared in Java. Attempting to assign to an instance of a Java class that does not exist will raise an error.

>>> x = java.util.ArrayList()
>>> x.language = 'english'
Traceback (innermost last):

  File "console", line 1, in ?

TypeError: can't set arbitrary attribute in java instance: language

To work around this limitation, you can create a subclass of the Java class -- it can be an empty subclass. You can then add attributes to instances of the subclass:

>>> class NewArrayList(java.util.ArrayList):
...     pass
... 
>>> x = NewArrayList()
>>> x.language = 'english'
>>> x.language
'english'

9. Serialization

Java objects cannot be serialized using the standard Python pickle and cPickle modules. Both Java and Jython objects can be serialized using normal Java serialization. However, when deserializing a Jython object you cannot use the normal java.io.ObjectInputStream class, you must use the Jython-specific org.python.util.PythonObjectInputStream, as shown here:

import java.io as io
import org.python.util as util

class TestClass(io.Serializable):
    def __init_ _(self, value=0):
        self.value = value
        self.message = "Serialized"

    def toString(self):
        return "Message: %(message)s value: 
          is %(value)s" % self.__dict_ _

instance = TestClass(3)

outFile = io.FileOutputStream("test.ser")
outStream = io.ObjectOutputStream(outFile)
outStream.writeObject(instance)
outFile.close( )

inFile = io.FileInputStream("test.ser")
inStream = util.PythonObjectInputStream(inFile)

readInstance = inStream.readObject( )
print readInstance.toString( )
Message: Serialized value: is 3

If you do not use the PythonObjectInputStream, you will get a runtime error because the ordinary Java ObjectInputStream has difficulty finding and recreating the dynamically loaded proxy classes used for Jython's Java inheritance.

10. Special Bonus Differences

Finally, here are a few of the most important differences between Jython and Cpython, not directly related to object usage:

For more information on Jython, see the main Jython site at http://www.jython.org, and get a copy of our recent book, Jython Essentials.

Noel Rappin has a Ph.D. in computer science from the Georgia Institute of Technology, where his research included methods for teaching Object-Oriented Programming and Design. He has extensive production experience in both Java and Python.


Return to the Python DevCenter.

Copyright © 2009 O'Reilly Media, Inc.