Podpora skriptovacích jazyků v JDK6 a OpenJDK6 (2.část)

21. 4. 2011
Doba čtení: 23 minut

Sdílet

V našem seriálu budeme pokračovat v popisu aplikačního programového rozhraní (API) navrženého v rámci JSR 223, které slouží k podpoře skriptovacích jazyků běžících nad virtuálním strojem Javy (JVM). Dnes se zaměříme především na podrobnější popis kooperace mezi aplikacemi v Javě a skripty v JavaScriptu.

Obsah

1. Získání základních informací o syntaxi vybraného skriptovacího ja­zyka

2. První demonstrační příklad – základní informace o syntaxi JavaScriptu

3. Chování javovských objektů v JavaScriptu či dalších skriptovacích jazycích

4. Druhý demonstrační příklad – výpis všech vlastností javovského objektu

5. Třetí demonstrační příklad – základní způsob volání getterů a setterů

6. Čtvrtý demonstrační příklad – zjednodušený způsob volání getterů a setterů

7. Pátý demonstrační příklad – JavaScript a přetížené metody v javovském objektu

8. Komentář k výsledkům předchozího demonstračního příkladu

9. Odkazy na Internetu

1. Získání základních informací o syntaxi vybraného skriptovacího ja­zyka

V předchozí části seriálu o technologiích používaných ve virtuálním stroji programovacího jazyka Java (JVM – Java Virtual Machine) jsme se seznámili se základními rysy aplikačního programového rozhraní navrženého v rámci JSR 223. Toto aplikační rozhraní je určeno pro zjednodušení a současně i unifikaci při oboustranné komunikaci mezi aplikací naprogramovanou přímo v Javě a skriptem (většinou spouštěným přímo z této aplikace, i když je možné skript spouštět i z „shellu“), který může být vytvořen v jakémkoli programovacím jazyce, pro nějž je dostupný skriptovací engine – důležité je to, že aplikační programové rozhraní, které vývojář vytvářející program v Javě používá, zůstává stejné bez ohledu na to, jaký skriptovací jazyk je ve skutečnosti využíván. Společně s proprietární Oracle JDK a taktéž OpenJDK je jeden ze skriptovacích enginů dodáván jako standardní součást distribuce JDKJRE. Jeho jméno je Rhino a programovacím jazykem, který je tímto skriptovacím enginem implementován, je populární, ale i zatracovaný a mnohými programátory nepochopený JavaScript.

Minule jsme si ukázali, že před zavoláním skriptu je nejdříve nutné získat vhodný skriptovací engine. Pro jeho získání se nejdříve vytváří instance třídy ScriptEngineMa­nager a poté je s využitím metod definovaných v této třídě, především metod getEngineByNa­me(), getEngineByEx­tension() či getEngineByMi­meType() získán objekt, který představuje skriptovací engine (tento objekt je instancí třídy implementující rozhraní ScriptEngine). Podrobnější informace o skriptovacím enginu je možné zjistit s využitím další třídy implementující rozhraní ScriptEngineFac­tory, která je na skriptovací engine navázána. Některé metody předepsané tímto rozhraním jsme si již ukázali minule, ovšem zbývá nám popis tří metod, jež je možné využít pro zjištění základních informací o syntaxi vybraného skriptovacího jazyka. Jedná se o metody getOutputState­ment(), getMethodCallSyn­tax() a getProgram(), které budou součástí demonstračního příkladu uvedeného ve druhé kapitole.

2. První demonstrační příklad – základní informace o syntaxi JavaScriptu

Metoda ScriptEngineFac­tory.getOutput­Statement() slouží ke zjištění toho, jakým způsobem se ve skriptu může vytisknout hodnota nějaké konstanty či proměnné. Příkaz pro výstup konstanty či proměnné (obecně výsledku nějakého výrazu) se samozřejmě v jednotlivých skriptovacích jazycích může lišit, ať již jménem příslušné funkce, tak i konkrétním způsobem zápisu; viz například rozdíl mezi javascriptovskou syntaxí:

print("Hello")

a syntaxí používanou v Closure (Lisp):

(print "Hello")

Této metodě se ve formě řetězce předá libovolný výraz (jehož syntaxe se většinou ani nekontroluje) ve formě řetězce a metoda vrátí tvar korektního příkazu, kterým je možné tento výraz ve vybraném skriptovacím jazyce vytisknout na standardní výstup a/nebo konzoli integrovaného vývojového prostředí. Obsah výsledného řetězce je samozřejmě závislý na tom, který skriptovací jazyk je použit.

Podobným způsobem pracuje metoda ScriptEngineFac­tory.getMethod­CallSyntax(), která pro zadané jméno objektu (řetězec), jméno metody (taktéž řetězec) a seznam parametrů vrátí syntakticky korektní volání této metody i se všemi předanými parametry. Poslední z těchto podpůrných metod nabízených rozhraním ScriptEngineFac­tory je metoda ScriptEngineFac­tory.getProgram(), která na základě zadané posloupnosti příkazů (opět předaných ve formě řetězců) vrátí syntakticky korektní reprezentaci skriptu, který tyto příkazy zavolá – teprve zde se například za jednotlivé příkazy vloží v případě JavaScriptu středníky. Podívejme se na demonstrační příklad, kde jsou všechny tři výše zmíněné metody využity:

import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;

/**
 * Ukazka pouziti metod:
 * <ul>
 * <li>ScriptEngineFactory.getOutputStatement()</li>
 * <li>ScriptEngineFactory.getMethodCallSyntax()</li>
 * <li>ScriptEngineFactory.getProgram()</li>
 * </ul>
 *
 * @author Pavel Tisnovsky
 */
public class ScriptingTest6 {

    /**
      * Test metody ScriptEngineFactory.getOutputStatement()
      */
    private static void getOutputStatementTest(ScriptEngineFactory factory) {
        System.out.println(factory.getOutputStatement("Hello world"));
    }

    /**
      * Test metody ScriptEngineFactory.getMethodCallSyntax()
      */
    private static void getMethodCallSyntaxTest(ScriptEngineFactory factory) {
        System.out.println(factory.getMethodCallSyntax("Math", "atan2", "x", "y"));
    }

    /**
      * Test metody ScriptEngineFactory.getProgram()
      */
    private static void getProgramTest(ScriptEngineFactory factory) {
        System.out.println(factory.getProgram("let a=10", "a=a+1", "print(a)"));
    }

    /**
      * Inicializace a spusteni testu.
      */
    private static void runTests() {
        ScriptEngineManager engineManager = new ScriptEngineManager();
        ScriptEngine scriptEngine = engineManager.getEngineByName("JavaScript");
        ScriptEngineFactory factory = scriptEngine.getFactory();

        getOutputStatementTest(factory);
        getMethodCallSyntaxTest(factory);
        getProgramTest(factory);
    }

    public static void main(String[] args) {
        runTests();
    }

}

Po spuštění tohoto demonstračního příkladu v JRE dodávaného s IcedTea6–1.8 získáme následující výstup (který by však neměl být diametrálně odlišný ani na dalších variantách JRE):

print("Hello world")
Math.atan2(x,y)
let a=10;a=a+1;print(a);

3. Chování javovských objektů v JavaScriptu či dalších skriptovacích jazycích

V předchozí části tohoto seriálu jsme si taktéž velmi stručně řekli, jak lze spustit program napsaný v JavaScriptu, i to, jak je možné tomuto programu předat parametry, popř. jak může program (funkce) napsaný v JavaScriptu vrátit objekt prakticky libovolného typu jako svoji návratovou hodnotu. Ovšem tato problematika není tak jednoduchá, jak by se při pohledu na aplikační programové rozhraní definované v JSR 223 mohlo na první pohled zdát :-) Možné problémy nastávají především ve chvíli, kdy je jako skriptovací jazyk zvolen takový programovací jazyk, který používá dynamické typování, tj. datové typy jsou při běhu skriptu přiřazovány přímo k hodnotám a nikoli k proměnným, což je oproti staticky typované Javě velký rozdíl – nikoli syntaktický, ale zejména sémantický. Navíc je v mnoha skriptovacích jazycích funkce chápána jako plnohodnotný datový typ, takže je možné funkce předávat jako parametry jiným funkcím, vracet funkce jako návratovou hodnotu, přiřazovat funkce do proměnných, atributů objektů (properties) atd.

Na začátku tohoto seriálu [1] [2] bylo uvedeno, že podobná funkcionalita (zejména chápání funkcí jako plnohodnotných objektů) je do JDK zaváděna v rámci projektu Lambda, ovšem na její stabilizaci a rozšíření si budeme muset ještě chvíli počkat (JDK 8?). Mezi úlohy prováděné skrytě prakticky každým skriptovacím enginem tedy patří i konverze a vůbec vyhodnocování datových typů a taktéž správné určení toho, které metody javovských objektů se mají zavolat ve chvíli, kdy se jedná o metody přetížené. Zajímavé je, že skriptovací engine tuto činnost provádí výhradně v čase běhu aplikace, kdy využívá například reflexi, na rozdíl od samotné Javy, u níž se některé (ne všechny!) kontroly a konverze mohou provádět již v době překladu programu do bajtkódu. V následujících třech demonstračních příkladech si některé možnosti nabízené skriptovacím enginem Rhino ukážeme. Bude se jednat o zjištění všech viditelných atributů a metod libovolného javovského objektu a volání metod tohoto objektu, včetně zjednodušeného způsobu volání setterů a getterů.

4. Druhý demonstrační příklad – výpis všech vlastností javovského objektu

V následujícím demonstračním příkladu je ukázáno, že skriptovací engine skutečně vyhodnocuje typy objektů i jejich vlastnosti (properties) až v čase běhu aplikace. Tento příklad je v podstatě velmi jednoduchý: funkci printAllObjec­tProperties napsané v JavaScriptu je předán jakýkoli objekt vytvořený buď v Javě nebo přímo v JavaScriptu a tato funkce následně vypíše všechny viditelné atributy a metody tohoto objektu (ty se v JavaScriptu nazývají properties; jedná se o položky uložené v asociativním poli, kterým je objekt implementován). Povšimněte si, že v tomto případě není příliš rozlišováno mezi atributy a metodami, což však koresponduje s tím, co jsme si již řekli v předchozí kapitole – funkce a tím pádem i metody (=metody s implicitním parametrem this/self) jsou „pouze“ dalším plnohodnotným datovým typem (používání properties z JavaScriptu je tedy v tomto ohledu jednodušší, než reflection API v Javě :-).

V JavaScriptu je dokonce možné získat i „hodnotu“ celého objektu, což vede k volání metody toString(), u níž je zaručeno, že vždy existuje, protože je děděna již z třídy Object. Následuje výpis zdrojového kódu dnešního druhého demonstračního příkladu, k jehož úspěšnému překladu a spuštění je navíc nutné přeložit i pomocnou třídu TestClass7.java, jejíž zdrojový kód je umístěn pod zdrojovým kódem příkladu:

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

/**
 * Vypis vsech viditelnych atributu a metod javovskeho
 * objektu z JavaScriptu.
 *
 * @author Pavel Tisnovsky
 */
public class ScriptingTest7 {

    /**
      * Zavolani javascriptove funkce, ktera vypise seznam
      * vsech viditelnych atributu a metod objektu, ktery
      * je funkci predan jako parametr.
      */
    private static void objectPropertiesTest(ScriptEngine engine) throws ScriptException, NoSuchMethodException {
        final String script =
            // pomocna funkce, ktera od sebe
            // vizualne oddelu jednotlive vypisy
            "function horizontalBreak() {" +
            "    for (i = 0; i<40; i++) {" +
            "        print('-');" +
            "    }" +
            "    println('');" +
            "}" +
            // funkce pro vypis vsech properties
            // objektu, ktery je ji predan jako
            // parametr
            "function printAllObjectProperties(object) {" +
            "    println('Object.toString(): ' + object);" +
            "    for (var prop in object) {" +
            "        println('Property: ' + prop);" +
            "    }" +
            "    horizontalBreak();" +
            "}";

        engine.eval(script);

        // Metoda invokeFunction() se musi volat pres rozhrani Invocable.
        Invocable invocable = (Invocable) engine;
        invocable.invokeFunction("printAllObjectProperties", new Object());
        invocable.invokeFunction("printAllObjectProperties", new TestClass7());
        invocable.invokeFunction("printAllObjectProperties", new StringBuffer());
        invocable.invokeFunction("printAllObjectProperties", 42);
        invocable.invokeFunction("printAllObjectProperties", new java.util.Date());
    }

    /**
      * Inicializace a spusteni testu.
      */
    private static void runTests() {
        ScriptEngineManager engineManager = new ScriptEngineManager();
        ScriptEngine scriptEngine = engineManager.getEngineByName("JavaScript");

        try {
            objectPropertiesTest(scriptEngine);
        }
        catch (ScriptException e) {
            e.printStackTrace();
        }
        catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        runTests();
    }

}

Zdrojový kód pomocné třídy TestClass7.java:

/**
  * Pomocna testovaci trida pouzita testem
  * ScriptingTest7
  */
public class TestClass7 {
    // Atributy s ruznymi pristupovymi pravy
    @SuppressWarnings("unused")
    private int privateAttribute;
    protected int protectedAttribute;
    int packageProtectedAttribute;
    public int publicAttribute;

    // Metody s ruznymi pristupovymi pravy
    @SuppressWarnings("unused")
    private void privateMethod() {
    }

    protected void protectedMethod() {
    }

    void packageProtectedMethod() {
    }

    public void publicMethod() {
    }

    /**
      * Tato metoda je mj. pouzita i v JavaScriptu
      * pri tisku retezcove podoby objektu.
      */
    @Override
    public String toString() {
        return "This is TestClass7";
    }
}

Zajímavé je zjistit, které metody a atributy jsou skutečně po spuštění příkladu vypsány. Jako malý úkol se pokuste odhadnout, proč jsou vypsány jen metody a atributy s viditelností public a jaké to má důsledky například pro bezpečnost aplikací:

Object.toString(): java.lang.Object@a401c2
Property: notifyAll
Property: toString
Property: equals
Property: class
Property: wait
Property: hashCode
Property: getClass
Property: notify
----------------------------------------
Object.toString(): This is TestClass7
Property: notifyAll
Property: toString
Property: equals
Property: class
Property: wait
Property: publicMethod
Property: hashCode
Property: publicAttribute
Property: getClass
Property: notify
----------------------------------------
Object.toString():
Property: getChars
Property: capacity
Property: insert
Property: codePointBefore
Property: indexOf
Property: setCharAt
Property: notifyAll
Property: length
Property: trimToSize
Property: reverse
Property: charAt
Property: subSequence
Property: equals
Property: notify
Property: codePointAt
Property: class
Property: ensureCapacity
Property: chars
Property: appendCodePoint
Property: wait
Property: deleteCharAt
Property: toString
Property: hashCode
Property: lastIndexOf
Property: append
Property: codePointCount
Property: delete
Property: offsetByCodePoints
Property: setLength
Property: replace
Property: substring
Property: getClass
----------------------------------------
Object.toString(): 42
----------------------------------------
Object.toString(): Tue Apr 19 22:22:38 CEST 2011
Property: setHours
Property: month
Property: getHours
Property: toString
Property: year
Property: wait
Property: hours
Property: getMinutes
Property: toLocaleString
Property: getClass
Property: setSeconds
Property: hashCode
Property: class
Property: seconds
Property: notify
Property: getTime
Property: getDay
Property: setTime
Property: getDate
Property: setDate
Property: clone
Property: day
Property: equals
Property: getTimezoneOffset
Property: getYear
Property: setYear
Property: setMinutes
Property: before
Property: getSeconds
Property: after
Property: timezoneOffset
Property: minutes
Property: time
Property: notifyAll
Property: date
Property: compareTo
Property: setMonth
Property: toGMTString
Property: getMonth
----------------------------------------

5. Třetí demonstrační příklad – základní způsob volání getterů a setterů

Dnešní třetí demonstrační příklad vlastně neukazuje nic nového. V příkladu jsou vytvořeny dva skripty obsahující funkce s jedním parametrem, pomocí něhož je javascriptovým funkcím předán objekt vytvořený v Javě. Funkce ve skriptu bez jakékoli kontroly (která by se musela provádět až v čase běhu aplikace, například kontrolou existence properties a jejich typu) zavolá setter a getter pro objekt, u nějž se předpokládá, že je instancí třídy TestClass8. U objektu, který by měl být instancí třídy java.awt.Color se zavolají gettery getRed(), getGreen(), getBlue() a getAlpha(). Ve skutečnosti nejsou předpoklady o typech objektů uvedené v předchozích dvou větách zcela korektní, protože v JavaScriptu postačuje, aby byl oběma funkcím předán libovolný objekt, který má implementovány všechny uvedené settery a gettery a nemusí se nutně jednat o instance tříd TestClass8 a java.awt.Color:

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

/**
 * Test, zda je mozne v javascriptovem programu volat settery
 * a gettery pomoci nasledujici syntaxe:
 * <pre>
 * object.getXXX()
 * object.setXXX(newValue)
 * </pre>
 *
 * @author Pavel Tisnovsky
 */
public class ScriptingTest8 {

    /**
      * Zavolani javascriptove funkce, jejimz parametrem
      * je instance tridy TestClass8.
      */
    private static void methodCallTest(ScriptEngine engine) throws ScriptException, NoSuchMethodException {
        final String script =
            "function callMethods(object) {" +
            "    println('Object.toString(): ' + object);" +
            "    println('old value: ' + object.getX());" +
            "    object.setX(42);" +
            "    println('new value: ' + object.getX());" +
            "}";

        engine.eval(script);
        ((Invocable)engine).invokeFunction("callMethods", new TestClass8());
    }

    /**
      * Zavolani nekterych getteru instance tridy jawa.awt.Color
      * z javascriptu.
      */
    private static void gettersTest(ScriptEngine engine) throws ScriptException, NoSuchMethodException {
        final String script =
            "function callColorGetters() {" +
            "    var color = new java.awt.Color(0x123456);" +
            "    println('Object.toString(): ' + color);" +
            "    println('red:   ' + color.getRed());" +
            "    println('blue:  ' + color.getBlue());" +
            "    println('green: ' + color.getGreen());" +
            "    println('alpha: ' + color.getAlpha());" +
            "}";

        engine.eval(script);

        // Metoda invokeFunction() se musi volat pres rozhrani Invocable.
        ((Invocable)engine).invokeFunction("callColorGetters");
    }

    /**
      * Inicializace a spusteni testu.
      */
    private static void runTests() {
        ScriptEngineManager engineManager = new ScriptEngineManager();
        ScriptEngine scriptEngine = engineManager.getEngineByName("JavaScript");

        try {
            methodCallTest(scriptEngine);
            System.out.println("----------------------------------");
            gettersTest(scriptEngine);
        }
        catch (ScriptException e) {
            e.printStackTrace();
        }
        catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        runTests();
    }

}

Pro správnou funkci tohoto demonstračního příkladu je nutné přeložit i pomocnou třídu TestClass8, která obsahuje příslušné settery i gettery, které jsou z příkladu volány:

/**
  * Pomocna testovaci trida pouzita testy
  * ScriptingTest8 a ScriptingTest9
  */
public class TestClass8 {
    private int x = 0;

    public void setX(int x) {
        System.out.println("Setter called with parameter " + x);
        this.x = x;
    }

    public int getX() {
        System.out.println("Getter called, returning value " + this.x);
        return this.x;
    }

    /**
      * Tato metoda je mj. pouzita i v JavaScriptu
      * pri tisku retezcove podoby objektu.
      */
    @Override
    public String toString() {
        return "This is TestClass8 containing attribute: " + this.x;
    }
}

Tento demonstrační příklad po svém spuštění vypíše na standardní výstup následující informace:

Object.toString(): This is TestClass8 containing attribute: 0
Getter called, returning value 0
old value: 0
Setter called with parameter 42
Getter called, returning value 42
new value: 42
----------------------------------
Object.toString(): java.awt.Color[r=18,g=52,b=86]
red:   18
blue:  86
green: 52
alpha: 255

6. Čtvrtý demonstrační příklad – zjednodušený způsob volání getterů a setterů

Předchozí demonstrační příklad byl v tomto článku uveden především jako ukázka základního způsobu, jakým lze přistupovat k setterům a getterům v JavaScriptu syntakticky prakticky shodným způsobem, jako je tomu v samotné Javě. Ovšem javascriptoví programátoři mají k dispozici ještě jeden způsob, který je jednodušší a pro mnohé vývojáře i čitelnější. Namísto volání objekt.getX() či objekt.setX(..) lze totiž v JavaScriptu použít taktéž následující dvě možnosti: objekt[‚x‘] a objekt.x (což jsou sémanticky stejné výrazy), kde za x lze doplnit jméno setteru či getteru bez předpony „set“ či „get“, přičemž se první písmeno názvu setteru/getteru změní na malé.

Předpokladem pro správnou činnost těchto příkazů je to, aby objekt neobsahoval viditelný atribut stejného jména, což by však při korektním programování v Javě nastat vůbec nemělo :-) Ostatně to, že se ve všech čtyřech případech (objekt.getX(), objekt.setX(), objekt[‚x‘] a objekt.x) skutečně volají gettery a settery, lze jednoduše ověřit – postačuje změnit jméno nastavovaného či čteného atributu, popř. do setterů a getterů přidat ladicí informace, což je i náš případ:

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

/**
 * Test, zda je mozne v javascriptovem programu volat settery
 * a gettery objektu vytvorenych v Jave pomoci nasledujici syntaxe:
 * <pre>
 * var foo = objekt['atribut'];
 * objekt['atribut'] = 'bar';
 * var foo = objekt.atribut;
 * objekt.atribut = 'bar';
 * </pre>
 *
 * @author Pavel Tisnovsky
 */
public class ScriptingTest9 {

    /**
      * Zavolani javascriptove funkce, jejimz parametrem
      * je instance tridy TestClass8.
      */
    private static void methodCallTest(ScriptEngine engine) throws ScriptException, NoSuchMethodException {
        final String script =
            "function callMethods(object) {" +
            "    println('Object.toString(): ' + object);" +
            "    println('old value: ' + object.x);" +
            "    object.x = 42;" +
            "    println('new value: ' + object.x);" +
            "}";

        engine.eval(script);

        // Metoda invokeFunction() se musi volat pres rozhrani Invocable.
        ((Invocable)engine).invokeFunction("callMethods", new TestClass8());
    }

    /**
      * Zavolani nekterych getteru instance tridy jawa.awt.Color
      * z javascriptu.
      */
    private static void gettersTest(ScriptEngine engine) throws ScriptException, NoSuchMethodException {
        final String script =
            "function callColorGetters() {" +
            "    var color = new java.awt.Color(0x123456);" +
            "    println('Object.toString(): ' + color);" +
            "    println('red:   ' + color['red']);" +
            "    println('blue:  ' + color['blue']);" +
            "    println('green: ' + color['green']);" +
            "    println('alpha: ' + color['alpha']);" +
            "    println('red:   ' + color.red);" +
            "    println('blue:  ' + color.blue);" +
            "    println('green: ' + color.green);" +
            "    println('alpha: ' + color.alpha);" +
            "}";

        engine.eval(script);

        // Metoda invokeFunction() se musi volat pres rozhrani Invocable.
        ((Invocable)engine).invokeFunction("callColorGetters");
    }

    /**
      * Inicializace a spusteni testu.
      */
    private static void runTests() {
        ScriptEngineManager engineManager = new ScriptEngineManager();
        ScriptEngine scriptEngine = engineManager.getEngineByName("JavaScript");

        try {
            methodCallTest(scriptEngine);
            System.out.println("----------------------------------");
            gettersTest(scriptEngine);
        }
        catch (ScriptException e) {
            e.printStackTrace();
        }
        catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        runTests();
    }

}

Tento demonstrační program po svém spuštění vypíše prakticky ty stejné informace, jako demonstrační program předchozí, pouze se opakují informace získané pomocí getterů nad objektem Color (protože jsou gettery volané dvakrát, pokaždé s jinou syntaxí zápisu):

Object.toString(): This is TestClass8 containing attribute: 0
Getter called, returning value 0
old value: 0
Setter called with parameter 42
Getter called, returning value 42
new value: 42
----------------------------------
Object.toString(): java.awt.Color[r=18,g=52,b=86]
red:   18
blue:  86
green: 52
alpha: 255
red:   18
blue:  86
green: 52
alpha: 255

7. Pátý demonstrační příklad – JavaScript a přetížené metody v javovském objektu

V dnešním pátém (v celkovém pořadí osmém :-) demonstračním příkladu je ukázáno chování enginu Rhino při rozhodování o tom, která přetížená metoda se má zavolat z JavaScriptu. V tomto příkladu je využita pomocná třída TestClass10 obsahující přetíženou a viditelnou metodu nazvanou overloaded, která je volána ze skriptu s různými typy parametrů. Komentář k chování enginu Rhino bude uveden pod zdrojovými kódy:

ict ve školství 24

import java.awt.Color;
import java.util.Date;

import javax.script.Invocable;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;
import javax.script.ScriptException;

public class ScriptingTest10 {

    private static void overloadedMethodCallTest(ScriptEngine scriptEngine) throws ScriptException, NoSuchMethodException {
        final String script =
            "function accessJavaObject(object) {" +
            "    object.overloaded(true);" +
            "    object.overloaded(false);" +
            "    object.overloaded(object == null);" +
            "    object.overloaded(new java.lang.Boolean(true));" +
            "    object.overloaded(new java.lang.Boolean(false));" +
            "    object.overloaded(new java.lang.Boolean(object == null));" +
            "    object.overloaded(new java.util.Date());" +
            "    object.overloaded(new Date());" +
            "    object.overloaded(new java.awt.Color(0));" +
            "    object.overloaded('Hello world');" +
            "    object.overloaded(42);" +
            "    object.overloaded(1/3.0);" +
            "    object.overloaded(new java.awt.Frame());" +
            "}";
        scriptEngine.eval(script);
        ((Invocable)scriptEngine).invokeFunction("accessJavaObject", new TestClass10());
    }

    private static Object callOverloadedMethod(ScriptEngine scriptEngine, Object value) throws ScriptException, NoSuchMethodException {
        return ((Invocable)scriptEngine).invokeFunction("valueReturnedFromMethod", new TestClass10(), value);
    }

    private static void overloadedMethodCallTest2(ScriptEngine scriptEngine) throws ScriptException, NoSuchMethodException {
        final String script =
            "function valueReturnedFromMethod(object, value) {" +
            "    return object.overloaded(value);" +
            "}";
        scriptEngine.eval(script);
        System.out.println(callOverloadedMethod(scriptEngine, 42));
        System.out.println(callOverloadedMethod(scriptEngine, 1.0/3));
        System.out.println(callOverloadedMethod(scriptEngine, true));
        System.out.println(callOverloadedMethod(scriptEngine, Boolean.valueOf(true)));
        System.out.println(callOverloadedMethod(scriptEngine, new Boolean(true)));
        System.out.println(callOverloadedMethod(scriptEngine, new Date()));
        System.out.println(callOverloadedMethod(scriptEngine, new Color(0x123456)));
        System.out.println(callOverloadedMethod(scriptEngine, new StringBuffer("hello world")));
    }

    /**
     * Inicializace a spusteni testu.
     */
    private static void runTests() {
        ScriptEngineManager engineManager = new ScriptEngineManager();
        ScriptEngine scriptEngine = engineManager.getEngineByName("JavaScript");

        try {
            overloadedMethodCallTest(scriptEngine);
            System.out.println("---------------------------------");
            overloadedMethodCallTest2(scriptEngine);
        }
         catch (ScriptException e) {
            e.printStackTrace();
        }
        catch (NoSuchMethodException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) {
        runTests();
    }

}

Pomocná třída TestClass10 má následující obsah:

import java.awt.Color;
import java.util.Date;

/**
  * Pomocna testovaci trida pouzita testem
  * ScriptingTest10
  */
public class TestClass10 {
    public String overloaded(Object o) {
        System.out.println("overloaded(object) called with parameter "+ o);
        return "Object";
    }

    public String overloaded(String o) {
        System.out.println("overloaded(string) called with parameter " + o);
        return "String";
    }

    public String overloaded(int o) {
        System.out.println("overloaded(int) called with parameter " + o);
        return "Integer";
    }

    public String overloaded(double o) {
        System.out.println("overloaded(double) called with parameter " + o);
        return "Double";
    }

    public String overloaded(Date o) {
        System.out.println("overloaded(Date) called with parameter " + o);
        return "Date";
    }

    public String overloaded(Color o) {
        System.out.println("overloaded(Color) called with parameter " + o);
        return "Color";
    }

    public String overloaded(boolean o) {
        System.out.println("overloaded(boolean) called with parameter " + o);
        return "boolean";
    }

    public String overloaded(Boolean o) {
        System.out.println("overloaded(Boolean) called with parameter " + o);
        return "Boolean";
    }

    @Override
    public String toString() {
        return "This is TestClass";
    }

}

8. Komentář k výsledkům předchozího demonstračního příkladu

Výsledky vytištěné po spuštění předchozího demonstračního příkladu jsou v několika ohledech zajímavé. Především je vidět, že skriptovací engine dokáže rozlišit mezi primitivním datovým typem boolean a příslušnou „obalovou“ třídou Boolean. Taktéž se korektně provede změna javascriptového objektu Date na typ java.util.Date, ovšem na druhou stranu se v použité verzi skriptovacího enginu všechny číselné proměnné převádí na typ double (popř. na obalovou třídu Double), což znamená, že se nezavolá metoda overloaded(int), a to ani tehdy, když je při volání použita celočíselná konstanta:

overloaded(boolean) called with parameter true
overloaded(boolean) called with parameter false
overloaded(boolean) called with parameter false
overloaded(Boolean) called with parameter true
overloaded(Boolean) called with parameter false
overloaded(Boolean) called with parameter false
overloaded(Date) called with parameter Tue Apr 19 23:01:35 CEST 2011
overloaded(Date) called with parameter Tue Apr 19 23:01:35 CEST 2011
overloaded(Color) called with parameter java.awt.Color[r=0,g=0,b=0]
overloaded(string) called with parameter Hello world
overloaded(double) called with parameter 42.0
overloaded(double) called with parameter 0.3333333333333333
overloaded(object) called with parameter java.awt.Frame[frame0,0,0,0x0,invalid,hidden,layout=java.awt.BorderLayout,title=,resizable,normal]
---------------------------------
overloaded(double) called with parameter 42.0
Double
overloaded(double) called with parameter 0.3333333333333333
Double
overloaded(boolean) called with parameter true
boolean
overloaded(boolean) called with parameter true
boolean
overloaded(boolean) called with parameter true
boolean
overloaded(Date) called with parameter Tue Apr 19 23:01:35 CEST 2011
Date
overloaded(Color) called with parameter java.awt.Color[r=18,g=52,b=86]
Color
overloaded(object) called with parameter hello world
Object

9. Odkazy na Internetu

  1. JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
    http://www.jcp­.org/en/jsr/de­tail?id=223
  2. Scripting for the Java Platform
    http://java.sun­.com/developer/techni­calArticles/J2SE/Des­ktop/scriptin­g/
  3. Scripting for the Java Platform (Wikipedia)
    http://en.wiki­pedia.org/wiki/Scrip­ting_for_the_Ja­va_Platform
  4. Java Community Process
    http://en.wiki­pedia.org/wiki/Ja­va_Specificati­on_Request
  5. Package javax.script (JavaDoc)
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/package-summary.html
  6. javax.script.Bin­dings
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/Bindings.html
  7. javax.script.Com­pilable
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/Compilable.html
  8. javax.script.In­vocable
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/Invocable.html
  9. javax.script.Scrip­tContext
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/ScriptContex­t.html
  10. javax.script.Scrip­tEngine
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/ScriptEngine­.html
  11. javax.script.Scrip­tEngineFactory
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/ScriptEngine­Factory.html
  12. javax.script.Ab­stractScriptEn­gine
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/AbstractScrip­tEngine.html
  13. javax.script.Com­piledScript
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/CompiledScrip­t.html
  14. javax.script.Scrip­tEngineManager
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/ScriptEngine­Manager.html
  15. javax.script.Sim­pleBindings
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/SimpleBindin­gs.html
  16. javax.script.Sim­pleScriptContext
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/SimpleScrip­tContext.html
  17. javax.script.Scrip­tException
    http://downlo­ad.oracle.com/ja­vase/6/docs/a­pi/javax/scrip­t/ScriptExcep­tion.html
  18. The Java Compatibility Test Tools: JavaTest Harness
    http://java.sun­.com/developer/techni­calArticles/JCPto­ols2/
  19. JavaScript engine (Wikipedia)
    http://en.wiki­pedia.org/wiki/Ja­vaScript_engi­ne
  20. Rhino (JavaScript engine)
    http://en.wiki­pedia.org/wiki/Rhi­no_(JavaScrip­t_engine)
  21. Rhino: JavaScript for Java
    http://www.mo­zilla.org/rhi­no/
  22. Java HotSpot VM Options
    http://www.ora­cle.com/technet­work/java/java­se/tech/vmopti­ons-jsp-140102.html
  23. HugePages
    http://linux-mm.org/HugePages
  24. Tuning big java heap and linux huge pages
    http://www.ti­kalk.com/alm/fo­rums/tuning-big-java-heap-and-linux-huge-pages
  25. How do I set up hugepages in Red Hat Enterprise Linux 4
    http://magazi­ne.redhat.com/2007/05­/29/how-do-i-set-up-hugepages-in-red-hat-enterprise-linux-4/
  26. Java SE Tuning Tip: Large Pages on Windows and Linux
    http://blogs.sun­.com/dagastine/en­try/java_se_tu­ning_tip_large
  27. Translation lookaside buffer
    http://en.wiki­pedia.org/wiki/Tran­slation_looka­side_buffer
  28. Physical Address Extension
    http://en.wiki­pedia.org/wiki/Phy­sical_Address_Ex­tension
  29. Java HotSpot VM Options
    http://www.ora­cle.com/technet­work/java/java­se/tech/vmopti­ons-jsp-140102.html
  30. Amdahl's law
    http://en.wiki­pedia.org/wiki/Am­dahl_law
  31. Garbage collection (computer science)
    http://en.wiki­pedia.org/wiki/Gar­bage_collecti­on_(computer_sci­ence)
  32. Dr. Dobb's | G1: Java's Garbage First Garbage Collector
    http://www.drdob­bs.com/article/prin­tableArticle.jhtml?ar­ticleId=219401061­&dept_url=/ja­va/
  33. Java's garbage-collected heap
    http://www.ja­vaworld.com/ja­vaworld/jw-08–1996/jw-08-gc.html
  34. Compressed oops in the Hotspot JVM
    http://wikis.sun­.com/display/Hot­SpotInternals/Com­pressedOops
  35. 32-bit or 64-bit JVM? How about a Hybrid?
    http://blog.ju­ma.me.uk/2008/10/­14/32-bit-or-64-bit-jvm-how-about-a-hybrid/
  36. Compressed object pointers in Hotspot VM
    http://blogs.sun­.com/nike/entry/com­pressed_objec­t_pointers_in_hot­spot
  37. Java HotSpot™ Virtual Machine Performance Enhancements
    http://downlo­ad.oracle.com/ja­vase/7/docs/techno­tes/guides/vm/per­formance-enhancements-7.html
  38. Using jconsole
    http://downlo­ad.oracle.com/ja­vase/1.5.0/doc­s/guide/manage­ment/jconsole­.html
  39. jconsole – Java Monitoring and Management Console
    http://downlo­ad.oracle.com/ja­vase/1.5.0/doc­s/tooldocs/sha­re/jconsole.html
  40. Great Computer Language Shootout
    http://c2.com/cgi/wi­ki?GreatCompu­terLanguageSho­otout
  41. x86–64
    http://en.wiki­pedia.org/wiki/X86–64
  42. Physical Address Extension
    http://en.wiki­pedia.org/wiki/Phy­sical_Address_Ex­tension
  43. Java performance
    http://en.wiki­pedia.org/wiki/Ja­va_performance
  44. 1.6.0_14 (6u14)
    http://www.ora­cle.com/technet­work/java/java­se/6u14–137039.html?ssSou­rceSiteId=otncn
  45. Update Release Notes
    http://www.ora­cle.com/technet­work/java/java­se/releasenotes-136954.html
  46. Java virtual machine: 4.10 Limitations of the Java Virtual Machine
    http://java.sun­.com/docs/book­s/jvms/second_e­dition/html/Clas­sFile.doc.html#88659
  47. Java™ Platform, Standard Edition 7 Binary Snapshot Releases
    http://dlc.sun­.com.edgesuite­.net/jdk7/bina­ries/index.html
  48. Trying the prototype
    http://mail.o­penjdk.java.net/pi­permail/lambda-dev/2010-August/002179.html
  49. Better closures (for Java)
    http://blogs.sun­.com/jrose/en­try/better_clo­sures
  50. Lambdas in Java: An In-Depth Analysis
    http://www.in­foq.com/articles/lam­bdas-java-analysis
  51. Class ReflectiveOpe­rationExcepti­on
    http://downlo­ad.java.net/jdk7/doc­s/api/java/lan­g/ReflectiveO­perationExcep­tion.html
  52. Proposal: Indexing access syntax for Lists and Maps
    http://mail.o­penjdk.java.net/pi­permail/coin-dev/2009-March/001108.html
  53. Proposal: Elvis and Other Null-Safe Operators
    http://mail.o­penjdk.java.net/pi­permail/coin-dev/2009-March/000047.html
  54. Java 7 : Oracle pushes a first version of closures
    http://www.bap­tiste-wicht.com/2010/05­/oracle-pushes-a-first-version-of-closures/
  55. Groovy: An agile dynamic language for the Java Platform
    http://groovy­.codehaus.org/O­perators
  56. Better Strategies for Null Handling in Java
    http://www.sli­deshare.net/Step­han.Schmidt/bet­ter-strategies-for-null-handling-in-java
  57. Control Flow in the Java Virtual Machine
    http://www.ar­tima.com/under­thehood/flowP­.html
  58. Java Virtual Machine
    http://en.wiki­pedia.org/wiki/Ja­va_virtual_machi­ne
  59. ==, .equals(), compareTo(), and compare()
    http://leepoin­t.net/notes-java/data/expres­sions/22compa­reobjects.html
  60. New JDK7 features
    http://openjdk­.java.net/pro­jects/jdk7/fe­atures/
  61. Project Coin: Bringing it to a Close(able)
    http://blogs.sun­.com/darcy/en­try/project_co­in_bring_close
  62. ClosableFinder source code
    http://blogs.sun­.com/darcy/re­source/Projec­tCoin/Closeable­Finder.java
  63. Joe Darcy blog about JDK
    http://blogs.sun­.com/darcy
  64. Java 7 – more dynamics
    http://www.bap­tiste-wicht.com/2010/04­/java-7-more-dynamics/
  65. ArrayList (JDK 1.4)
    http://downlo­ad.oracle.com/ja­vase/1.4.2/doc­s/api/java/util/A­rrayList.html

Autor článku

Vystudoval VUT FIT a v současné době pracuje na projektech vytvářených v jazycích Python a Go.