Контролирование Java из Jython


Так как вы имеете в своем распоряжении 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())