2016年3月23日水曜日

Scala から Jython で書いたクラスのオブジェクトを操作してみる

Scala から Jython で書いたクラスのオブジェクトを操作してみました。といっても Jython で書いたクラスのオブジェクトを Java で作成し、Scala でそのクラスオブジェクトを操作しています。

ほぼ「 Using Jython Within Java Applications 」に書いてある通りのことをやってます。

最後の main() の箇所のみ Scala に置き換えて実行してます。

英語のドキュメント読むの大変なので適当に読み飛ばしてます。なにか間違って解釈してたらゴメンナサイ。


環境

  • Ubuntu 14.04 (Trusty Tahr) Server
  • Scala 2.9.2
  • Jython 2.7.0
  • Java 1.7.0_95

Scala インストール

$ sudo apt-get install scala

Jython インストール


JythonObjectFactory を作成

Jython で書かれた様々なクラスを Java から呼び出すためのオブジェクトファクトリーを作成します。

mypackage/common/JythonObjectFactory.java 作成

$ cat mypackage/common/JythonObjectFactory.java
package mypackage.common;

import org.python.core.Py;
import org.python.core.PyObject;
import org.python.core.PySystemState;

/**
 * Jython Object Factory using PySystemState
 */
public class JythonObjectFactory {
    private final Class interfaceType;
    private final PyObject klass;

    // Constructor obtains a reference to the importer, module, and the class name
    public JythonObjectFactory(PySystemState state, Class interfaceType, String moduleName, String className) {
        this.interfaceType = interfaceType;
        PyObject importer = state.getBuiltins().__getitem__(Py.newString("__import__"));
        PyObject module = importer.__call__(Py.newString(moduleName));
        klass = module.__getattr__(className);
        System.err.println("module=" + module + ",class=" + klass);
    }

    // This constructor passes through to the other constructor
    public JythonObjectFactory(Class interfaceType, String moduleName, String className) {
        this(new PySystemState(), interfaceType, moduleName, className);
    }

    // All of the followng methods return
    // a coerced Jython object based upon the pieces of information
    // that were passed into the factory. The differences are
    // between them are the number of arguments that can be passed
    // in as arguents to the object.

    public Object createObject() {
        return klass.__call__().__tojava__(interfaceType);
    }

    public Object createObject(Object arg1) {
        return klass.__call__(Py.java2py(arg1)).__tojava__(interfaceType);
    }

    public Object createObject(Object arg1, Object arg2) {
        return klass.__call__(Py.java2py(arg1), Py.java2py(arg2)).__tojava__(interfaceType);
    }

    public Object createObject(Object arg1, Object arg2, Object arg3) {
        return klass.__call__(Py.java2py(arg1), Py.java2py(arg2),
            Py.java2py(arg3)).__tojava__(interfaceType);
    }

    public Object createObject(Object args[], String keywords[]) {
        PyObject convertedArgs[] = new PyObject[args.length];
        for (int i = 0; i < args.length; i++) {
            convertedArgs[i] = Py.java2py(args[i]);
        }

        return klass.__call__(convertedArgs, keywords).__tojava__(interfaceType);
    }

    public Object createObject(Object... args) {
        return createObject(args, Py.NoKeywords);
    }
}

コンパイル

$ javac -classpath /.../some/where/jython.jar mypackage/common/JythonObjectFactory.java
  • classpath はインストールした Jython の jython.jar への絶対パスを指定します。

コンパイルにより作成された JythonObjectFactory.class は一度作成したら変更することはありません。


Jython スクリプトを作成

building_module.py 作成

$ cat building_module.py
from mypackage.interfaces import BuildingType

class BuildingClass(BuildingType):

    def __init__(self):
        self.name = None
        self.address = None
        self.id = -1

    def getBuildingName(self):
        return self.name

    def setBuildingName(self, name):
        self.name = name;

    def getBuildingAddress(self):
        return self.address

    def setBuildingAddress(self, address):
        self.address = address

    def getBuildingId(self):
        return self.id

    def setBuildingId(self, id):
        self.id = id

Scala ではこの building_module の BuildingClass オブジェクトを操作します。

from mypackage.interfaces import BuildingType で BuildingType をインポートし、 BuildingClass を BuildingType から派生しています。

Java ではメソッドに渡す引き数や戻り値の型が明示されている必要があるので BuildingType が必要なようです。

この BuildingType を宣言する mypackage/interfaces/BuildingType.java 作成

$ cat mypackage/interfaces/BuildingType.java
package mypackage.interfaces;

public interface BuildingType {
    public String getBuildingName();
    public String getBuildingAddress();
    public int getBuildingId();
    public void setBuildingName(String name);
    public void setBuildingAddress(String address);
    public void setBuildingId(int id);
}
  • BuildingType の 各メソッド (getBuildingName() とか) は Jython の BuildingClass の各メソッドに対応しています。

コンパイル

$ javac mypackage/interfaces/BuildingType.java

Scala で BuildingClass オブジェクトを操作

作成したフィル構成は以下のようになってます。

$ tree -a
.
├── building_module.py
└── mypackage
        ├── common
        │   ├── JythonObjectFactory.class
        │   └── JythonObjectFactory.java
        └── interfaces
                ├── BuildingType.class
                └── BuildingType.java

3 directories, 5 files

Scala 実行

$ scala -classpath /.../some/where/jython.jar

インポート

scala> import mypackage.common.JythonObjectFactory
scala> import mypackage.interfaces.BuildingType

BuildingClass オブジェクト作成

scala> var factory = new JythonObjectFactory(classOf[BuildingType], "building_module", "BuildingClass")
scala> var building = factory.createObject().asInstanceOf[BuildingType]

BuildingClass オブジェクト操作

scala> building.setBuildingName("TestName")
scala> building.setBuildingAddress("TestAddress")
scala> building.setBuildingId(123)

scala> println(building.getBuildingId() + " " + building.getBuildingName() + " " + building.getBuildingAddress())
123 TestName TestAddress

0 件のコメント:

コメントを投稿