Так как вы имеете в своем распоряжении Java, и вы можете установить и получить значение для интерпретатора, есть огромное количество того, что вы можете выполнить, используя описанный ранее подход (управляя Python из Java). Но одна из удивительных вещей относительно Jython состоит в том, что он делает Java классы почти прозрачно доступными изнутри Jython. Обычно Java классы выглядят, как и Python классы. Это верно для классов из стандартных библиотек Java так же точно, как и для классов, который вы создаете сами, как это видно в приведенном ниже примере:
## interpreter:JavaClassInPython.py
#=M jython.bat JavaClassInPython.py
# Использование классов Java изнутри Jython
from java.util import Date, HashSet, HashMap
from interpreter.javaclass import JavaClass
from math import sin
d = Date() # Создание объекта Java Date
print d # Вызов toString()
# "Генератор" легко создаст данные:
class ValGen:
def __init__(self, maxVal):
self.val = range(maxVal)
# Вызывается во время итерации 'for':
def __getitem__(self, i):
# Возвращается запись из двух элементов:
return self.val[i], sin(self.val[i])
# Стандартные Java контейнеры:
map = HashMap()
set = HashSet()
for x, y in ValGen(10):
map.put(x, y)
set.add(y)
set.add(y)
print map
print set
# Итерация по множеству:
for z in set:
print z, z.__class__
print map[3] # Используем индекситование Python словаря
for x in map.keySet(): # keySet() метод из Map
print x, map[x]
# Использование классов Java, которые вы создали сами,
# очень просто:
jc = JavaClass()
jc2 = JavaClass("Created within Jython")
print jc2.getVal()
jc.setVal("Using a Java class is trivial")
print jc.getVal()
print jc.getChars()
jc.val = "Using bean properties"
print jc.val
##~
Комментарий "=M" распознается инструментом генерации mekefile'а (который я создал для этой книги) для замены команды makefile. Он будет использоваться взамен команд, которые инструмент извлечения обычно помещает в makefile.
Обратите внимание, что выражение import относится к структуре пакетов Java именно так, как этого следовало ожидать. В первом примере создается объект Date( ), как будто это родной класс Python, и происходит распечатка этого объекта простым вызовом toString( ).
ValGen реализует концепцию "генератора", который использует отличную идею из C++ STL (Стандартная Библиотека Шаблонов, часть стандартной библиотеки C++). Генератор является объектом, который производит новый объект при каждом вызове "метода генерации", и он достаточно удобен для заполнения контейнеров. Далее я использую его в итераторе for, так что мне нужен метод генерации, чтобы он вызывался в процессе итерации. Это специальный метод, называемый __getitem__( ), который фактически является перегруженным оператором для индексирования '[ ]'. В цикле for этот метод вызывается каждый раз, когда есть необходимость продвинутся к следующему элементу, а когда элементы заканчиваются, __getitem__( ) выбрасывает исключение выхода за пределы диапазона, тем самым сигнализируя об окончании цикла for (в других языках вы никогда не использовали исключения для обычного управления процессом выполнения, но в Python это выглядит достаточно хорошо). Это исключение возникает автоматически, когда self.val[i] выходит за пределы элементов, что упрощает код __getitem( ). Сложность заключается только в том, что __getitem__( ) казалось бы возвращает два объекта вместо одного. Python автоматически пакует множественные возвращаемые значения в запись (структуру), так что с другой стороны вы получаете единый объект (в C++ или Java вы должны создавать собственную структуру данных, чтобы сделать это). Кроме того, в цикле for, где используется Valgen, Python автоматически "распаковывает" запись, так что вы имеете множество итераторов в цикле for. Упрощения синтаксиса такого рода делает Python столь любимым.
Объекты map и set являются экземплярами Java классов HasMap и HashSet, опять таки созданных так, как будто они являются родными компонентами Python. В цикле for методы put( ) и add( ) работают так же, как это делается в Java. Так что индексирование в Java Map использует ту же нотацию, что и для словарей, но заметьте, что для прохождения по ключам Map вы должны использовать метод Map keySet( ), а не метод keys( ) для словарей Python.
Заключительная часть примера показывает использование Java класса, который я создал для интереса, чтобы продемонстрировать, как это просто. Обратите внимание, что Jython интуитивно понимает свойства JavaBean, так что вы можете использовать методы getVal( ) и setVal( ) или присваивать и читать из эквивалентного val-свойства. Кроме того, getChars( ) возвращает Character[] в Java, который становится массивом в Python.
Простейший способ использования Java классов, которые вы создали, внутри Python программ состоит в помещении их внутрь пакетов. Хотя Jython может также импортировать Java классы не входящие в пакет (import JavaClass), все такие не входящие в пакет классы будут трактоваться, как если бы они были определены в различных пакетах, так что они смогут видеть только публичные методы друг друга.
Java пакеты транслируются в модули Python, и Python должен импортировать модули, чтобы быть способным использовать классы Java. Ниже приведен код для JavaClass:
//- interpreter:javaclass:JavaClass.java
package interpreter.javaclass;
import junit.framework.*;
import com.bruceeckel.util.*;
public class JavaClass {
private String s = "";
public JavaClass() {
System.out.println("JavaClass()");
}
public JavaClass(String a) {
s = a;
System.out.println("JavaClass(String)");
}
public String getVal() {
System.out.println("getVal()");
return s;
}
public void setVal(String a) {
System.out.println("setVal()");
s = a;
}
public Character[] getChars() {
System.out.println("getChars()");
Character[] r = new Character[s.length()];
for (int i = 0; i < s.length(); i++)
r[i] = new Character(s.charAt(i));
return r;
}
public static class Test extends TestCase {
JavaClass x1 = new JavaClass(), x2 = new JavaClass("UnitTest");
public void test1() {
System.out.println(x2.getVal());
x1.setVal("SpamEggsSausageAndSpam");
System.out.println(Arrays2.toString(x1.getChars()));
}
}
public static void main(String[] args) {
junit.textui.TestRunner.run(Test.class);
}
} // /:~
Вы можете видеть, что это обычный Java класс без каких-либо указаний, что он будет использоваться в Jython программе. По этой причине один из важнейiих этапов использования Jython состоит в тестировании Java кода [10]. Поскольку Python является достаточно мощным, гибким, динамичным языком - это идеальный инструмент для автоматизированного тестирования рабочей среды без добавления и изменения тестируемого Java кода.
Внутренние классы
Внутренние классы становятся атрибутами объекта класса. Экземпляры статического внутреннего класса могут быть созданы при помощи обычного вызова:
com.foo.JavaClass.StaticInnerClass()
Не статические внутренние классы должны иметь внешний экземпляр класса, указываемый как первый аргумент:
com.foo.JavaClass.InnerClass(com.foo.JavaClass())
← | Управление интерпретатором | Использование библиотек Java | → |