Programovací jazyk Clojure 13: překlad programů z Clojure do bajtkódu JVM (pokračování)

4. 9. 2012
Doba čtení: 37 minut

Sdílet

Dnes se budeme společně zabývat stejným tématem jako v části předchozí – řekneme si další informace o tom, jakým způsobem se překládají aplikace naprogramované v Clojure do bajtkódu JVM. Jeden z problémů, které je nutné řešit, spočívá v tom, jak do Clojure vnést informaci o datových typech.

Obsah

1. Programovací jazyk Clojure 13: překlad programů z Clojure do bajtkódu JVM (pokračování)

2. Tvorba tříd v Clojure a jejich překlad do bajtkódu (makro gen-class)

3. Využití prefixů u názvů funkcí

4. Staticky definované datové typy v dynamicky typovaném programovacím jazyce?

5. Tvorba metod z funkcí

6. Informace o typech předávaných parametrů i o návratovém typu metody

7. Rozhraní clojure.lang.IFn a další magie skrytá pod povrchem

8. Statické metody

9. Odkazy na Internetu

1. Programovací jazyk Clojure 13: překlad programů z Clojure do bajtkódu JVM (pokračování)

V předchozí části seriálu o Javě i o virtuálním stroji Javy jsme si řekli základní informace o způsobu překladu aplikací naprogramovaných v Clojure do bajtkódu JVM. Připomeňme si, že překlad lze provést několika způsoby. V průběhu vývoje je nejčastěji prováděn překlad s generováním bajtkódu přímo ve virtuálním stroji Javy, což znamená, že se nevytváří žádné externí soubory s bajtkódem, které by bylo možné prozkoumat či přenést na počítače uživatelů. Z tohoto důvodu je pro účely distribuce aplikace a/nebo nasazení aplikace většinou vhodnější vedle zdrojových kódů dodávat i explicitně přeloženou aplikaci, a to buď ve formě souborů .class s bajtkódem, nebo alternativně ve formě Java archivu (.jar) či webového archivu (.war) – tyto archivy totiž kromě dalších souborů obsahují právě přeložený bajtkód. Existuje ještě třetí způsob překladu – vytváření tříd a k nim příslušných souborů .class přímo za běhu aplikace přes proxy třídy, což je varianta, kterou lze v samotné Javě napodobit jen velmi neohrabaně s využitím classloaderů a třídy java.lang.Compiler.

Minule jsme si taktéž ukazovali, jak se „explicitní“ překlad funkcí (prozatím skutečně pouze funkcí) provádí prakticky. V současné verzi programovacího jazyka Clojure je možné přeložit pouze kód uložený ve zdrojovém souboru, nelze tedy například přímo v interpretru přeložit jednu zvolenou funkci atd. (výjimkou je překlad využívající již zmíněnou proxy třídu; tento způsob bude popsaný příště). V nejjednodušším případě, pokud tedy prozatím nebudeme vytvářet třídy umístěné v balíčcích, je možné mít soubory se zdrojovými texty uložené v podadresáři nazvaném classes. Tyto soubory by měly mít koncovku .clj a na začátku každého souboru by měla být uvedena specifikace jmenného prostoru s názvem odpovídajícím jménu souboru se zdrojovým textem. Následující zdrojový text by tedy měl být uložený v souboru se jménem classes/AddTest1.clj:

(ns AddTest1)
 
(defn add
    [x y]
    (+ x y))

Překlad tohoto zdrojového textu do bajtkódu JVM se provede z adresáře, v němž je umístěn výše zmíněný podadresář classes. V tomto adresáři je pro co největší jednoduchost (snadné nastavení CLASSPATH) uložen i archiv clojure-1.4.0.jar, popř. symbolický link na tento archiv. Struktura celého pracovního adresáře by mohla vypadat následovně:

|   clojure-1.4.0.jar
|

AddTest1.clj
\---

Překlad programu AddTest1 do bajtkódu se provede tak, že se nejdříve spustí interpret programovacího jazyka Clojure s tím, že se na CLASSPATH přidá i podadresář classes:

java -cp .;classes;clojure-1.4.0.jar clojure.main

Jakmile se interpret spustí, postačí zadat jediný příkaz spouštějící překlad:

user=> (compile 'AddTest1)
AddTest1
user=>

V podadresáři classes by se měly vytvořit tři nové soubory, takže se celá struktura pracovního adresáře změní takto:

|   clojure-1.4.0.jar
|

AddTest1$add.class
AddTest1$loading__4784__auto__.class
AddTest1.clj
AddTest1__init.class
\---

Stručný význam jednotlivých souborů uložených v podadresáři classes:

# Soubor Velikost Popis
1 AddTEst1.clj 55 zdrojový kód příkladu v Clojure
2 AddTest1__init.class 2369 pomocné metody využívané „interpretrem“
3 AddTest1$loading__4784__auto__.class 1492 pomocné metody využívané „interpretrem“
4 AddTest1$add.class 884 metoda add a třída, která tuto metodu obaluje

2. Tvorba tříd v Clojure a jejich překlad do bajtkódu (makro gen-class)

Překlad demonstračního příkladu AddTest1, s nímž jsme se seznámili v předchozí kapitole, sice skutečně vedl k vytvoření několika souborů .class a tedy i k vytvoření několika javovských tříd, ovšem ve skutečnosti v tomto případě překladač pouze vygeneroval pro každou definovanou funkci samostatnou třídu obalující příslušnou metodu. K funkci nazvané add, která byla definována ve jmenném prostoru AddTest1 tedy byla vytvořena třída nazvaná AddTest1$add, což přesně odpovídá jmenným konvencím Javy. Pro zajímavost se můžeme podívat na vnitřní strukturu této třídy. Vzhledem k tomu, že vygenerovaný bajtkód je plnohodnotným bajtkódem JVM, lze pro zjištění jeho vnitřní struktury použít libovolný (vhodný) nástroj, například nástroj javap, s nímž jsme se již v tomto seriálu několikrát setkali. Připomeňme si, že tento nástroj je standardní součástí každé instalace JDK:

% javap AddTest1$add
 
Compiled from "AddTest1.clj"
public final class AddTest1$add extends clojure.lang.AFunction{
    public static final clojure.lang.Var const__0;
    public static {};
    public AddTest1$add();
    public java.lang.Object invoke(java.lang.Object, java.lang.Object);
}

Naše funkce původně nazvaná add se ve skutečnosti bude volat nepřímo přes metodu Object invoke(Object, Object). Toto chování překladače Clojure sice docela dobře odpovídá filozofii funkcionálního programovacího jazyka (obecné „beztypové“ parametry), ale v praxi budeme většinou potřebovat generovat „skutečné“ javovské třídy a budeme tedy muset vyřešit minimálně dva problémy – jak sloučit několik funkcí do jediné třídy (a udělat z těchto funkcí statické či nestatické metody) a jak nahradit obecné metody typu Object invoke(Object, Object) za metody s přesněji specifikovanými datovými typy. Obě tyto problematiky lze v programovacím jazyce Clojure samozřejmě řešit, i když se popravdě řečeno nejedná o řešení elegantní: je zde patrné, že vzájemná kooperace mezi objektově orientovaným staticky typovaným jazykem a dynamicky typovaným funkcionálním jazykem je sice možná, ale nikoli zcela automatická. Nejprve si ukažme, jak se vytváří „skutečné“ třídy. Pro tyto účely se používá téměř všemocné makro nazvané gen-class.

V následujícím demonstračním příkladu je makro gen-class voláno v rámci zpracování makra ns, což je obvyklé doporučované řešení. Makro gen-class může mít velké množství volitelných parametrů, z nichž my použijeme prozatím pouze parametry dva – určení jména třídy (:name „AddTest2“) a zajištění vygenerování metody main (:main true). Ve zdrojovém kódu je navíc definována i funkce -main překládaná do metody main. Znak pomlčky na začátku názvu této metody nebyl zapsán omylem, jeho význam si vysvětlíme v navazující kapitole:

(ns AddTest2 (
    :gen-class
    :name "AddTest2"
    :main true))
 
(defn -main
    []
    (println "Hello world!"))

Překlad se provede stejným postupem, jako tomu bylo i u předchozího demonstračního příkladu:

% java -cp .;classes;clojure-1.4.0.jar clojure.main
 
user=> (compile 'AddTest2)
AddTest2
user=>

V adresáři classes by nyní mělo být uloženo i těchto pět souborů:

# Soubor Velikost
1 AddTest2.clj 128
2 AddTest2__init.class 2325
3 AddTest2$loading__4784__auto__.class 1494
4 AddTest2$_main.class 848
5 AddTest2.class 1744

Vzhledem k tomu, že ve třídě AddTest2.class je definována i metoda public static void main(String[]), můžeme tuto metodu ihned po inicializaci virtuálního stroje spustit, a to prakticky stejně, jako jakoukoli jinou aplikaci napsanou v Javě (pouze stále potřebujeme mít archiv clojure*.jar uložen na CLASSPATH):

% java -cp .;classes;clojure-1.4.0.jar AddTest2
Hello world!

3. Využití prefixů u názvů funkcí

V demonstračním příkladu z předchozí kapitoly byl před názvem funkce main uveden znak pomlčky, který byl nedílnou součástí tohoto názvu. Jedná se o takzvaný prefix používaný pro poloautomatické začleňování funkcí do vytvářených tříd, kde se z funkcí stanou plnohodnotné statické či nestatické metody. Pokud vám nevyhovuje implicitní prefix - (tj. ona pomlčka), je samozřejmě možné ho změnit a to s využitím nepovinného parametru :prefix předaného makru gen-class. V dnešním třetím demonstračním příkladu je tento parametr použit s hodnotou (řetězcem) „implementation-“, což samozřejmě znamená, že se musel změnit i název funkce main-main (prefixem je jen pomlčka) na implementation-main (prefixem je slovo „implementation“ následované pomlčkou):

(ns AddTest3 (
    :gen-class
    :name "AddTest3"
    :main true
    :prefix "implementation-"))
 
(defn implementation-main
    []
    (println "Hello world!"))

Překlad tohoto demonstračního příkladu se provede naprosto stejným způsobem, jako tomu bylo v předchozím demonstračním příkladu (v dalších kapitolách už si tuto informaci nebudeme uvádět, ale pro jednoduchost předpokládejme, že všechny zdrojové kódy budou vždy umístěny v podadresáři classes):

% java -cp .;classes;clojure-1.4.0.jar clojure.main
 
user=> (compile 'AddTest3)
AddTest3
user=>

Výsledkem překladu je čtveřice nových souborů obsahujících bajtkód a v následující tabulce jsou pro porovnání uvedena jména a velikosti souborů s bajtkódem vygenerovaným pro demonstrační příklad AddTest2 i pro příklad AddTest3. Povšimněte si, že změny postihly název bajtkódu s implementací funkce main, zatímco dalších pomocných tříd se změny prakticky nedotkly:

# Soubor (AddTest2) Velikost (AddTest2) Soubor (AddTest3) Velikost (AddTest3)
1 AddTest2.clj 128 AddTest3.clj 173
2 AddTest2__init.class 2325 AddTest3__init.class 2353
3 AddTest2$loading__4784__auto__.class 1494 AddTest3$loading__4784__auto__.class 1494
4 AddTest2$_main.class 848 AddTest3$_main.class 862
5 AddTest2.class 1744 AddTest3.class 1828

Zajímavější než porovnání bajtkódů vzniklých překladem obou demonstračních příkladů však bude interní struktura třídy AddTest3.class:

% javap AddTest3
 
public class AddTest3 extends java.lang.Object{
    public static {};
    public AddTest3();
    public java.lang.String toString();
    public boolean equals(java.lang.Object);
    public java.lang.Object clone();
    public int hashCode();
    public static void main(java.lang.String[]);
}

Z předchozího výpisu je patrné, že se v této třídě skutečně nachází metoda public static void main(String[]) – tudíž je možné tuto metodu spustit po startu JVM – a navíc jsou v této třídě „poctivě“ implementovány i metody toString(), equals(), hashCode() a clone() získané překrytím metod se stejnou signaturou, které jsou deklarovány ve třídě Object, tedy ve třídě představující kořen stromu třídní hierarchie Javy. Pokud se však podíváme na obsah těchto překrytých metod, zjistíme, že se v nich ve skutečnosti volá některá z forem metody Object invoke(Object…) definované v rozhraní clojure.lang.IFn, což znamená nutnost přetypování výsledku atd. To je daň, kterou platíme za to, že se snažíme používat bajtkód navržený pro staticky typovaný jazyk. Další informace o této problematice si řekneme v navazujících kapitolách.

4. Staticky definované datové typy v dynamicky typovaném programovacím jazyce?

Dále uvedené informace není v žádném případě nutné znát pro to, abyste byli schopni vytvářet v Clojure skutečné třídy. Jedná se jen o problematiku, s níž musí bojovat a nějak ji vyřešit všichni tvůrci dynamicky typovaných programovacích jazyků určených pro běh nad virtuálním strojem Javy.

Nyní se již dostáváme k poměrně závažnému tématu – jakým způsobem je vlastně v bajtkódu zařízeno volání funkcí (či možná přesněji řečeno metod) naprogramovaných původně v Clojure? Největší problém spočívá v tom, jak se pracuje s typy parametrů a současně i s typem návratové hodnoty, protože programovací jazyk Java i jeho bajtkód je navržen s ohledem na to, že se při předávání parametrů metodám i při výpočtu aritmetických a logických výrazů používají primitivní datové typy (boolean, char, byte, short, int, long, float, double), popř. se při volání metod předávají hodnoty referenčního typu, neboli reference na objekty. Důležitější však je, že informace o typech a o počtu parametrů i informace o typu návratové hodnoty je nedílnou součástí signatury každé metody a tato signatura je uložena v bajtkódu JVM (je dokonce přímo čitelná z constant poolu).

Při překladu funkcí z Clojure do bajtkódu JVM tedy musí překladač nějakým postupem vygenerovat metody se správnou signaturou a to takovým způsobem, aby se stále jednalo o plnohodnotné metody, které je možné zavolat s libovolným typem parametrů, tj. stejným způsobem, jakým můžeme volat funkce v Clojure (počet parametrů je na druhou stranu přesně zadaný již ve chvíli, kdy je metoda vytvořena). My ve skutečnosti vlastně již víme, jak je tento úkol vyřešen, protože již v první kapitole jsme si ukázali, že následující funkce:

(defn add
    [x y]
    (+ x y))

se do bajtkódu přeloží jako metoda se signaturou public java.lang.Object invoke(java.lang.Object, java.lang.Object);, která je umístěna do „obalové“ třídy, v našem případě mající název AddTest1$add:

% javap AddTest1$add
 
Compiled from "AddTest1.clj"
public final class AddTest1$add extends clojure.lang.AFunction{
    public static final clojure.lang.Var const__0;
    public static {};
    public AddTest1$add();
    public java.lang.Object invoke(java.lang.Object, java.lang.Object);
}

Metoda invoke tedy jako své parametry akceptuje libovolné dva objekty, což je přesně to, co jsme vlastně zpočátku požadovali, protože v Clojure je jakákoli numerická hodnota představována instancí nějaké třídy implementující rozhraní java.lang.Number. A skutečně – pokud se podíváme na bajtkód metody invoke, zjistíme, že se jedná o velmi jednoduchou metodu, která ve skutečnosti volá metodu clojure.lang.Numbers.add(Object, Object) (až do této chvíle je tedy možné předat metodě invoke jakoukoli dvojici objektů – opět stejně jako v samotném Clojure!):

public java.lang.Object invoke(java.lang.Object, java.lang.Object);
  Code:
   0:   aload_1         ; první parametr na zásobník
   1:   aconst_null     ; pravděpodobně jen zajištění, že se první parametr již nikde nepoužije
   2:   astore_1
   3:   aload_2         ; druhý parametr na zásobník
   4:   aconst_null     ; pravděpodobně jen zajištění, že se první parametr již nikde nepoužije
   5:   astore_2
   6:   invokestatic    #34; //Method clojure/lang/Numbers.add:(Ljava/lang/Objec
t;Ljava/lang/Object;)Ljava/lang/Number;
   9:   areturn
}

Jednoduše lze zjistit, že třída clojure.lang.Numbers skutečně obsahuje metodu add s touto signaturou:

Compiled from "Numbers.java"
public class clojure.lang.Numbers extends java.lang.Object{
    ...
    public static java.lang.Number add(java.lang.Object, java.lang.Object);
    ...
}

Až v této metodě je v runtime proveden test na skutečný typ předávaných objektů:

public static java.lang.Number add(java.lang.Object, java.lang.Object);
  Code:
   0:   aload_0
   1:   invokestatic    #2; //Method ops:(Ljava/lang/Object;)Lclojure/lang/Numbers$Ops;
   4:   aload_1
   5:   invokestatic    #2; //Method ops:(Ljava/lang/Object;)Lclojure/lang/Numbers$Ops;
   8:   invokeinterface #13,  2; //InterfaceMethod clojure/lang/Numbers$Ops.combine:(Lclojure/lang/Numbers$Ops;)Lclojure/lang/Numbers$Ops;
   13:  aload_0
   14:  checkcast       #3; //class java/lang/Number
   17:  aload_1
   18:  checkcast       #3; //class java/lang/Number
   21:  invokeinterface #14,  3; //InterfaceMethod clojure/lang/Numbers$Ops.add:(Ljava/lang/Number;Ljava/lang/Number;)Ljava/lang/Number;
   26:  areturn

5. Tvorba metod z funkcí

V této kapitole si ukážeme, jakým způsobem lze do generovaných tříd přidat nové metody, a to dokonce metody, u nichž je možné přesně specifikovat typ předávaných parametrů i typ návratové hodnoty. Pro přidání metod do vytvářené třídy (resp. přesněji řečeno do bajtkódu odpovídajícího této třídě) je nutné použít další parametr předávaný makru gen-class. Prozatím jsme se v předchozích kapitolách seznámili se třemi parametry tohoto makra: :name jméno_třídy, :main true/false a :prefix prefix_funkcí. Čtvrtým důležitým parametrem je parametr s názvem :methods, jemuž se předá vektor obsahující informace o všech metodách, které se mají do vytvářeného bajtkódu přidat. Informace o každé metodě je taktéž představována vektorem, jehož prvním prvkem je název vygenerované metody (bez prefixu), ve druhém prvku je uvedena specifikace typů parametrů metody (třetí vnořený vektor!) a prvkem třetím je pak typ návratové hodnoty metody. Zní to složitě, že? Ukažme si tedy, jak celý zápis :methods může vypadat:

:methods
[                          ; začátek vektoru obsahujícího informace o metodách
 
    [                      ; začátek vektoru popisujícího metodu se signaturou "long add(long, long)"
        add                ; jméno metody
        [long long]        ; TYP parametrů metody (nikoli názvy těchto parametrů)
        long               ; návratový typ metody
    ]                      ; konec vektoru popisujícího metodu se signaturou "long add(long, long)"
 
    [                      ; začátek vektoru popisujícího metodu se signaturou "String add(String, String)"
        add                ; jméno metody
        [String String]    ; TYP parametrů metody (nikoli názvy těchto parametrů)
        String             ; návratový typ metody
    ]                      ; konec vektoru popisujícího metodu se signaturou "String add(String, String)"
 
]                          ; konec vektoru obsahujícího informace o metodách

V praxi může použití :methods vypadat následovně – vytvoříme novou třídu nazvanou AddTest4, která bude obsahovat nestatickou metodu add akceptující dvojici parametrů typu long a jejíž návratová hodnota bude taktéž long. Tato metoda je předepsána funkcí implementation-add, kde nejsou prozatím uvedeny žádné informace o datových typech, jedná se tedy o obyčejnou funkci Clojure:

(ns AddTest4 (
    :gen-class
    :name "AddTest4"
    :prefix "implementation-"
    :methods [
        [add [long long] long] ]))
 
(defn implementation-add
    [x y]
    (+ x y))
 
(defn implementation-main
    []
    (println "Hello world!" (implementation-add 10 20)))

Jak tedy bude vypadat struktura vygenerovaného bajtkódu třídy AddTest4? S využitím nástroje javap můžeme snadno zjistit, že metoda add skutečně byla vytvořena a že její signatura přesně odpovídá našemu předpisu – public long add(long, long):

% javap AddTest4
 
public class AddTest4 extends java.lang.Object{
    public static {};
    public AddTest4();
    public java.lang.String toString();
    public boolean equals(java.lang.Object);
    public java.lang.Object clone();
    public int hashCode();
    public long add(long, long);
    public static void main(java.lang.String[]);
}

Ztratili jsme však něco z obecnosti funkce implementation-main, kterou lze z Clojure volat s jakoukoli dvojicí číselných hodnot? Ve skutečnosti tomu tak není, protože ve třídě AddTest4 je sice uložena metoda public long add(long, long); (viz výpis uvedený před tímto odstavcem), ale kromě toho byl vygenerován i bajtkód třídy pojmenované AddTest4$implementation_add, v níž je stále uložen obecný tvar této funkce volané přes metodu Object invoke(Object, Object):

% javap AddTest4$implementation_add
 
Compiled from "AddTest4.clj"
public final class AddTest4$implementation_add extends clojure.lang.AFunction{
    public static final clojure.lang.Var const__0;
    public static {};
    public AddTest4$implementation_add();
    public java.lang.Object invoke(java.lang.Object, java.lang.Object);
}

6. Informace o typech předávaných parametrů i o návratovém typu metody

Všechny funkce, které jsme prozatím v Clojure vytvářeli, obsahovaly pouze informaci o počtu a názvu parametrů těchto funkcí (tyto údaje se zapisují do vektoru umístěného ihned za název funkce), nikoli však již údaje o typu parametrů, což dobře odpovídá způsobu zápisu funkcí a/nebo metod v dynamicky typovaných jazycích. Ve skutečnosti je však možné i v dynamicky typovaném jazyku Clojure přidat k funkcím i „statickou“ informaci o předpokládaných typech parametrů i o typu návratové hodnoty. Pro tento účel se před jméno parametru popř. za jméno funkce může přidat zápis ve tvaru ^typ. Demonstrační příklad uvedený v předchozí kapitole tedy můžeme ještě vylepšit tím, že u funkce implementation_add určíme, že oba předávané parametry by měly být typu long a i návratová hodnota by měla být typu long, což ostatně přesně koresponduje s údaji uvedenými v parametru :methods makra gen-class:

(ns AddTest5 (
    :gen-class
    :name "AddTest5"
    :prefix "implementation-"
    :methods [[add [long long] long]]))
 
(defn implementation-add ^long
    [^long x ^long y]
    (+ x y))
 
(defn implementation-main
    []
    (println "Hello world!" (implementation-add 10 20)))

V bajtkódu vytvořené a přeložené třídy AddTest5 nenajdeme žádné „novinky“, které by nás mohly překvapit:

% javap AddTest5
 
public class AddTest5 extends java.lang.Object{
    public static {};
    public AddTest5();
    public java.lang.String toString();
    public boolean equals(java.lang.Object);
    public java.lang.Object clone();
    public int hashCode();
    public long add(long, long);
    public static void main(java.lang.String[]);
}

Mnohem zajímavější je však třída AddTest5$implementation_add, protože zde oproti předchozímu demonstračnímu příkladu došlo ke dvěma významným změnám. První změnou je to, že třída nově implementuje rozhraní s poněkud neobvyklým názvem clojure.lang.IF$LLL a druhou změnou je přidání nové nestatické metody long invokePrim(long, long):

% javap AddTest5$implementation_add
 
Compiled from "AddTest5.clj"
public final class AddTest5$implementation_add extends clojure.lang.AFunction
    ; nově implementované rozhraní
    implements clojure.lang.IFn$LLL{
    public static final clojure.lang.Var const__0;
    public static {};
    public AddTest5$implementation_add();
    ; nová metoda vyžadovaná rozhraním IFn$LLL
    public final long invokePrim(long, long);
    public java.lang.Object invoke(java.lang.Object, java.lang.Object);
}

Podrobnosti o tom, proč vlastně byla třída AddTest5$implementation_add vytvořena přesně takovýmto způsobem, si řekneme v navazující kapitole.

7. Rozhraní clojure.lang.IFn a další magie skrytá pod povrchem

V této kapitole jsou opět uvedeny informace, které sice není zapotřebí znát pro použití jazyka Clojure v praxi, ovšem současně tyto informace osvětlují některé aspekty interní struktury Clojure i důvod, proč je konstrukce překladače pro dynamicky typované jazyky pracující nad JVM relativně složitá a někdy málo elegantní (částečné řešení spočívá ve využití instrukce invokedynamic).

Podívejme se ještě jednou na vygenerovanou třídu AddTest5. Tato třída je odvozena od třídy clojure.lang.AFunction a navíc implementuje rozhraní IFn$LLL. V samotné třídě clojure.lang.AFunction pro nás v tuto chvíli není nic zajímavého až na informaci, že se jedná o specializaci třídy clojure.lang.AFn. Až tato třída je zajímavá a to především z toho důvodu, že implementuje rozhraní clojure.lang.IFn, v němž je předepsána celá řada variant metody invoke. Ostatně se o tom sami přesvědčte:

Compiled from "IFn.java"
public interface clojure.lang.IFn extends java.util.concurrent.Callable,java.lang.Runnable{
    public abstract java.lang.Object invoke();
    public abstract java.lang.Object invoke(java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object);
    public abstract java.lang.Object invoke(java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object, java.lang.Object[]);
    public abstract java.lang.Object applyTo(clojure.lang.ISeq);
}

Uff! Co to všechno vlastně pro nás znamená? Třída clojure.lang.AFn implementuje všechny výše vypsané metody rozhraní clojure.lang.IFn, ovšem nejedná se o finální metody, což znamená, že jakákoli třída odvozená od clojure.lang.AFn může libovolnou z těchto metod překrýt a implementovat ji po svém. Tímto poněkud „ukecaným“ způsobem je zajištěno to, že jakoukoli funkci lze v Clojure volat s libovolným množstvím parametrů (resp. s maximálně 21 parametry, pokud počítám dobře), jejichž typy se zjišťují až v runtime a současně implicitní implementace těchto metod zajišťuje správné chování funkcí při předání špatného počtu parametrů – nesmíme totiž zapomenout na to, že funkce jsou vždy překládány do bajtkódu, a to i při používání interpretru, tedy REPL.

Druhá informace o implementovaném rozhraní IFn$LLL třídou AddTest5 je taktéž důležitá. V tomto rozhraní je totiž předepsána jediná metoda se dvěma parametry typu long vracející hodnotu typu long – to jsou ostatně ony tři „L“ v názvu rozhraní:

javap IFn$LLL
 
Compiled from "IFn.java"
public interface clojure.lang.IFn$LLL{
    public abstract long invokePrim(long, long);
}

Clojure existuje celá řada podobných rozhraní, která umožňují definovat signaturu metody invokePrim s různými typy parametrů a s různými typy návratové hodnot. Tyto informace lze vždy vyčíst z názvu rozhraní. Například rozhraní IFn$DLDOL předepisuje metodu invokePrim takto:

public interface clojure.lang.IFn$DLDOL{
    public abstract long invokePrim(double, long, double, java.lang.Object);
}

Prozatím existují tato rozhraní kombinující různé parametry a různé návratové hodnoty metody invokePrim:

clojure/lang/IFn$D         clojure/lang/IFn$DD        clojure/lang/IFn$DDD
clojure/lang/IFn$DDDD      clojure/lang/IFn$DDDDD     clojure/lang/IFn$DDDDL
clojure/lang/IFn$DDDDO     clojure/lang/IFn$DDDL      clojure/lang/IFn$DDDLD
clojure/lang/IFn$DDDLL     clojure/lang/IFn$DDDLO     clojure/lang/IFn$DDDO
clojure/lang/IFn$DDDOD     clojure/lang/IFn$DDDOL     clojure/lang/IFn$DDDOO
clojure/lang/IFn$DDL       clojure/lang/IFn$DDLD      clojure/lang/IFn$DDLDD
clojure/lang/IFn$DDLDL     clojure/lang/IFn$DDLDO     clojure/lang/IFn$DDLL
clojure/lang/IFn$DDLLD     clojure/lang/IFn$DDLLL     clojure/lang/IFn$DDLLO
clojure/lang/IFn$DDLO      clojure/lang/IFn$DDLOD     clojure/lang/IFn$DDLOL
clojure/lang/IFn$DDLOO     clojure/lang/IFn$DDO       clojure/lang/IFn$DDOD
clojure/lang/IFn$DDODD     clojure/lang/IFn$DDODL     clojure/lang/IFn$DDODO
clojure/lang/IFn$DDOL      clojure/lang/IFn$DDOLD     clojure/lang/IFn$DDOLL
clojure/lang/IFn$DDOLO     clojure/lang/IFn$DDOO      clojure/lang/IFn$DDOOD
clojure/lang/IFn$DDOOL     clojure/lang/IFn$DDOOO     clojure/lang/IFn$DL
clojure/lang/IFn$DLD       clojure/lang/IFn$DLDD      clojure/lang/IFn$DLDDD
clojure/lang/IFn$DLDDL     clojure/lang/IFn$DLDDO     clojure/lang/IFn$DLDL
clojure/lang/IFn$DLDLD     clojure/lang/IFn$DLDLL     clojure/lang/IFn$DLDLO
clojure/lang/IFn$DLDO      clojure/lang/IFn$DLDOD     clojure/lang/IFn$DLDOL
clojure/lang/IFn$DLDOO     clojure/lang/IFn$DLL       clojure/lang/IFn$DLLD
clojure/lang/IFn$DLLDD     clojure/lang/IFn$DLLDL     clojure/lang/IFn$DLLDO
clojure/lang/IFn$DLLL      clojure/lang/IFn$DLLLD     clojure/lang/IFn$DLLLL
clojure/lang/IFn$DLLLO     clojure/lang/IFn$DLLO      clojure/lang/IFn$DLLOD
clojure/lang/IFn$DLLOL     clojure/lang/IFn$DLLOO     clojure/lang/IFn$DLO
clojure/lang/IFn$DLOD      clojure/lang/IFn$DLODD     clojure/lang/IFn$DLODL
clojure/lang/IFn$DLODO     clojure/lang/IFn$DLOL      clojure/lang/IFn$DLOLD
clojure/lang/IFn$DLOLL     clojure/lang/IFn$DLOLO     clojure/lang/IFn$DLOO
clojure/lang/IFn$DLOOD     clojure/lang/IFn$DLOOL     clojure/lang/IFn$DLOOO
clojure/lang/IFn$DO        clojure/lang/IFn$DOD       clojure/lang/IFn$DODD
clojure/lang/IFn$DODDD     clojure/lang/IFn$DODDL     clojure/lang/IFn$DODDO
clojure/lang/IFn$DODL      clojure/lang/IFn$DODLD     clojure/lang/IFn$DODLL
clojure/lang/IFn$DODLO     clojure/lang/IFn$DODO      clojure/lang/IFn$DODOD
clojure/lang/IFn$DODOL     clojure/lang/IFn$DODOO     clojure/lang/IFn$DOL
clojure/lang/IFn$DOLD      clojure/lang/IFn$DOLDD     clojure/lang/IFn$DOLDL
clojure/lang/IFn$DOLDO     clojure/lang/IFn$DOLL      clojure/lang/IFn$DOLLD
clojure/lang/IFn$DOLLL     clojure/lang/IFn$DOLLO     clojure/lang/IFn$DOLO
clojure/lang/IFn$DOLOD     clojure/lang/IFn$DOLOL     clojure/lang/IFn$DOLOO
clojure/lang/IFn$DOO       clojure/lang/IFn$DOOD      clojure/lang/IFn$DOODD
clojure/lang/IFn$DOODL     clojure/lang/IFn$DOODO     clojure/lang/IFn$DOOL
clojure/lang/IFn$DOOLD     clojure/lang/IFn$DOOLL     clojure/lang/IFn$DOOLO
clojure/lang/IFn$DOOO      clojure/lang/IFn$DOOOD     clojure/lang/IFn$DOOOL
clojure/lang/IFn$DOOOO     clojure/lang/IFn$L         clojure/lang/IFn$LD
clojure/lang/IFn$LDD       clojure/lang/IFn$LDDD      clojure/lang/IFn$LDDDD
clojure/lang/IFn$LDDDL     clojure/lang/IFn$LDDDO     clojure/lang/IFn$LDDL
clojure/lang/IFn$LDDLD     clojure/lang/IFn$LDDLL     clojure/lang/IFn$LDDLO
clojure/lang/IFn$LDDO      clojure/lang/IFn$LDDOD     clojure/lang/IFn$LDDOL
clojure/lang/IFn$LDDOO     clojure/lang/IFn$LDL       clojure/lang/IFn$LDLD
clojure/lang/IFn$LDLDD     clojure/lang/IFn$LDLDL     clojure/lang/IFn$LDLDO
clojure/lang/IFn$LDLL      clojure/lang/IFn$LDLLD     clojure/lang/IFn$LDLLL
clojure/lang/IFn$LDLLO     clojure/lang/IFn$LDLO      clojure/lang/IFn$LDLOD
clojure/lang/IFn$LDLOL     clojure/lang/IFn$LDLOO     clojure/lang/IFn$LDO
clojure/lang/IFn$LDOD      clojure/lang/IFn$LDODD     clojure/lang/IFn$LDODL
clojure/lang/IFn$LDODO     clojure/lang/IFn$LDOL      clojure/lang/IFn$LDOLD
clojure/lang/IFn$LDOLL     clojure/lang/IFn$LDOLO     clojure/lang/IFn$LDOO
clojure/lang/IFn$LDOOD     clojure/lang/IFn$LDOOL     clojure/lang/IFn$LDOOO
clojure/lang/IFn$LL        clojure/lang/IFn$LLD       clojure/lang/IFn$LLDD
clojure/lang/IFn$LLDDD     clojure/lang/IFn$LLDDL     clojure/lang/IFn$LLDDO
clojure/lang/IFn$LLDL      clojure/lang/IFn$LLDLD     clojure/lang/IFn$LLDLL
clojure/lang/IFn$LLDLO     clojure/lang/IFn$LLDO      clojure/lang/IFn$LLDOD
clojure/lang/IFn$LLDOL     clojure/lang/IFn$LLDOO     clojure/lang/IFn$LLL
clojure/lang/IFn$LLLD      clojure/lang/IFn$LLLDD     clojure/lang/IFn$LLLDL
clojure/lang/IFn$LLLDO     clojure/lang/IFn$LLLL      clojure/lang/IFn$LLLLD
clojure/lang/IFn$LLLLL     clojure/lang/IFn$LLLLO     clojure/lang/IFn$LLLO
clojure/lang/IFn$LLLOD     clojure/lang/IFn$LLLOL     clojure/lang/IFn$LLLOO
clojure/lang/IFn$LLO       clojure/lang/IFn$LLOD      clojure/lang/IFn$LLODD
clojure/lang/IFn$LLODL     clojure/lang/IFn$LLODO     clojure/lang/IFn$LLOL
clojure/lang/IFn$LLOLD     clojure/lang/IFn$LLOLL     clojure/lang/IFn$LLOLO
clojure/lang/IFn$LLOO      clojure/lang/IFn$LLOOD     clojure/lang/IFn$LLOOL
clojure/lang/IFn$LLOOO     clojure/lang/IFn$LO        clojure/lang/IFn$LOD
clojure/lang/IFn$LODD      clojure/lang/IFn$LODDD     clojure/lang/IFn$LODDL
clojure/lang/IFn$LODDO     clojure/lang/IFn$LODL      clojure/lang/IFn$LODLD
clojure/lang/IFn$LODLL     clojure/lang/IFn$LODLO     clojure/lang/IFn$LODO
clojure/lang/IFn$LODOD     clojure/lang/IFn$LODOL     clojure/lang/IFn$LODOO
clojure/lang/IFn$LOL       clojure/lang/IFn$LOLD      clojure/lang/IFn$LOLDD
clojure/lang/IFn$LOLDL     clojure/lang/IFn$LOLDO     clojure/lang/IFn$LOLL
clojure/lang/IFn$LOLLD     clojure/lang/IFn$LOLLL     clojure/lang/IFn$LOLLO
clojure/lang/IFn$LOLO      clojure/lang/IFn$LOLOD     clojure/lang/IFn$LOLOL
clojure/lang/IFn$LOLOO     clojure/lang/IFn$LOO       clojure/lang/IFn$LOOD
clojure/lang/IFn$LOODD     clojure/lang/IFn$LOODL     clojure/lang/IFn$LOODO
clojure/lang/IFn$LOOL      clojure/lang/IFn$LOOLD     clojure/lang/IFn$LOOLL
clojure/lang/IFn$LOOLO     clojure/lang/IFn$LOOO      clojure/lang/IFn$LOOOD
clojure/lang/IFn$LOOOL     clojure/lang/IFn$LOOOO     clojure/lang/IFn$OD
clojure/lang/IFn$ODD       clojure/lang/IFn$ODDD      clojure/lang/IFn$ODDDD
clojure/lang/IFn$ODDDL     clojure/lang/IFn$ODDDO     clojure/lang/IFn$ODDL
clojure/lang/IFn$ODDLD     clojure/lang/IFn$ODDLL     clojure/lang/IFn$ODDLO
clojure/lang/IFn$ODDO      clojure/lang/IFn$ODDOD     clojure/lang/IFn$ODDOL
clojure/lang/IFn$ODDOO     clojure/lang/IFn$ODL       clojure/lang/IFn$ODLD
clojure/lang/IFn$ODLDD     clojure/lang/IFn$ODLDL     clojure/lang/IFn$ODLDO
clojure/lang/IFn$ODLL      clojure/lang/IFn$ODLLD     clojure/lang/IFn$ODLLL
clojure/lang/IFn$ODLLO     clojure/lang/IFn$ODLO      clojure/lang/IFn$ODLOD
clojure/lang/IFn$ODLOL     clojure/lang/IFn$ODLOO     clojure/lang/IFn$ODO
clojure/lang/IFn$ODOD      clojure/lang/IFn$ODODD     clojure/lang/IFn$ODODL
clojure/lang/IFn$ODODO     clojure/lang/IFn$ODOL      clojure/lang/IFn$ODOLD
clojure/lang/IFn$ODOLL     clojure/lang/IFn$ODOLO     clojure/lang/IFn$ODOO
clojure/lang/IFn$ODOOD     clojure/lang/IFn$ODOOL     clojure/lang/IFn$ODOOO
clojure/lang/IFn$OL        clojure/lang/IFn$OLD       clojure/lang/IFn$OLDD
clojure/lang/IFn$OLDDD     clojure/lang/IFn$OLDDL     clojure/lang/IFn$OLDDO
clojure/lang/IFn$OLDL      clojure/lang/IFn$OLDLD     clojure/lang/IFn$OLDLL
clojure/lang/IFn$OLDLO     clojure/lang/IFn$OLDO      clojure/lang/IFn$OLDOD
clojure/lang/IFn$OLDOL     clojure/lang/IFn$OLDOO     clojure/lang/IFn$OLL
clojure/lang/IFn$OLLD      clojure/lang/IFn$OLLDD     clojure/lang/IFn$OLLDL
clojure/lang/IFn$OLLDO     clojure/lang/IFn$OLLL      clojure/lang/IFn$OLLLD
clojure/lang/IFn$OLLLL     clojure/lang/IFn$OLLLO     clojure/lang/IFn$OLLO
clojure/lang/IFn$OLLOD     clojure/lang/IFn$OLLOL     clojure/lang/IFn$OLLOO
clojure/lang/IFn$OLO       clojure/lang/IFn$OLOD      clojure/lang/IFn$OLODD
clojure/lang/IFn$OLODL     clojure/lang/IFn$OLODO     clojure/lang/IFn$OLOL
clojure/lang/IFn$OLOLD     clojure/lang/IFn$OLOLL     clojure/lang/IFn$OLOLO
clojure/lang/IFn$OLOO      clojure/lang/IFn$OLOOD     clojure/lang/IFn$OLOOL
clojure/lang/IFn$OLOOO     clojure/lang/IFn$OOD       clojure/lang/IFn$OODD
clojure/lang/IFn$OODDD     clojure/lang/IFn$OODDL     clojure/lang/IFn$OODDO
clojure/lang/IFn$OODL      clojure/lang/IFn$OODLD     clojure/lang/IFn$OODLL
clojure/lang/IFn$OODLO     clojure/lang/IFn$OODO      clojure/lang/IFn$OODOD
clojure/lang/IFn$OODOL     clojure/lang/IFn$OODOO     clojure/lang/IFn$OOL
clojure/lang/IFn$OOLD      clojure/lang/IFn$OOLDD     clojure/lang/IFn$OOLDL
clojure/lang/IFn$OOLDO     clojure/lang/IFn$OOLL      clojure/lang/IFn$OOLLD
clojure/lang/IFn$OOLLL     clojure/lang/IFn$OOLLO     clojure/lang/IFn$OOLO
clojure/lang/IFn$OOLOD     clojure/lang/IFn$OOLOL     clojure/lang/IFn$OOLOO
clojure/lang/IFn$OOOD      clojure/lang/IFn$OOODD     clojure/lang/IFn$OOODL
clojure/lang/IFn$OOODO     clojure/lang/IFn$OOOL      clojure/lang/IFn$OOOLD
clojure/lang/IFn$OOOLL     clojure/lang/IFn$OOOLO     clojure/lang/IFn$OOOOD
clojure/lang/IFn$OOOOL

V předchozí části tohoto seriálu jsme si řekli, že Clojure využívá pouze malé množství primitivních typů jazyka Java a toto je jeden z důvodů, proč jich není podporováno více. Pokud by se například kromě typu double podporoval ještě typ float, musel by počet rozhraní IFn$*** být ještě mnohem větší!

8. Statické metody

Ve výčtu metod zapisovaného do parametru :methods makra gen-class lze určit, které metody jsou statické a které nikoli. Připomeňme si, že nestatické metody se od metod statických liší především tím, že se jim při volání automaticky předává i reference na objekt, pro nějž je nestatická metoda volána. Naproti tomu statické metody nemají přístup k atributům tohoto objektu (mají však samozřejmě přístup k atributům třídy). Pro vytvoření statické metody postačuje před vektor obsahující informace o metodě přidat metainformaci ^{:static true}. To je vše – pouze na základě této informace se vytvoří metoda, která má v bajtkódu nastaven příznak ACC_STATIC a bude se jí předávat o jeden parametr méně – bude tedy chybět neviditelný parametr this:

(ns AddTest6 (
    :gen-class
    :name "AddTest6"
    :prefix "implementation-"
    :methods [^{:static true} [add [long long] long]]))
 
(defn implementation-add
    [^long x ^long y]
    (^long + x y))
 
(defn implementation-main
    []
    (println "Hello world!" (implementation-add 10 20)))

O tom, že je skutečně vygenerována statická metoda, se můžeme jednoduše přesvědčit prozkoumáním interní struktury vytvořeného souboru AddTest6.class s bajkódem:

% javap AddTest6
 
public class AddTest6 extends java.lang.Object{
    public static {};
    public AddTest6();
    public java.lang.String toString();
    public boolean equals(java.lang.Object);
    public java.lang.Object clone();
    public int hashCode();
    public static long add(long, long);
    public static void main(java.lang.String[]);
}

Na závěr si musíme ještě říci, že přidání typů k funkcím (jedná se o metainformaci) v Clojure ve skutečnosti neznamená, že by bylo možné jen takto jednoduše vytvořit přetížené metody, tj. metody se stejným názvem, které se od sebe liší typem a/nebo počtem parametrů. Podívejme se na následující příklad, v němž je skrytá určitá zrada, kterou však překladač dopředu neohlásí, a to z toho prostého důvodu, že tuto zradu vlastně nemůže u dynamicky typovaného jazyka vždy na 100% předvídat:

bitcoin_skoleni

(ns AddTest7 (
    :gen-class
    :name "AddTest7"
    :prefix "implementation-"
    :methods [
        ^{:static true} [add [long long] long]
        ^{:static true} [add [double double] double]
        ^{:static true} [add [String String] String]
        ]))
 
(defn implementation-add
    [^long x ^long y]
    (^long + x y))
 
(defn implementation-add
    [^double x ^double y]
    (^double + x y))
 
(defn implementation-add
    [^String x ^String y]
    (^String str x y))
 
(defn implementation-main
    []
    (println "Hello world!"))

Mohlo by se zdát, že je vše v pořádku a že jsme takto jednoduše nadefinovali trojici funkcí implementation-add, které se přeloží do odpovídajících metod add. I průzkum bajtkódu vytvořené třídy neodhalí žádný problém, spíše naopak:

% javap AddTest7
 
public class AddTest7 extends java.lang.Object{
    public static {};
    public AddTest7();
    public java.lang.String toString();
    public boolean equals(java.lang.Object);
    public java.lang.Object clone();
    public int hashCode();
    public static long add(long, long);
    public static double add(double, double);
    public static java.lang.String add(java.lang.String, java.lang.String);
    public static void main(java.lang.String[]);
}

Ve skutečnosti však došlo k tomu, že poslední funkce implementation-add přemazala definici předchozích funkcí, a to úplně stejným způsobem, jako by se to stalo v interpretru jazyka Clojure. To znamená, že i když je ve vygenerovaném bajtkódu třídy AddTest7.class uvedena trojice metod add s různými parametry, bude se z těchto metod ve skutečnosti vždy volat přeložená funkce implementation-add, v jejímž těle se budou spojovat dva řetězce pomocí str. Pokud se této funkci předají parametry jiného typu než dvojice řetězců (AddTest7.add(1,2)), dojde k běhové chybě!

9. Odkazy na Internetu

  1. Tech behind Tech: Clojure Macros Simplified
    http://techbehindtech.com/2010/09/28/clo­jure-macros-simplified/
  2. Fatvat – Exploring functional programming: Clojure Macros
    http://www.fatvat.co.uk/2009/02/clo­jure-macros.html
  3. Eulerovo číslo
    http://cs.wikipedia.org/wi­ki/Eulerovo_číslo
  4. List comprehension
    http://en.wikipedia.org/wi­ki/List_comprehension
  5. List Comprehensions in Clojure
    http://asymmetrical-view.com/2008/11/18/list-comprehensions-in-clojure.html
  6. Clojure Programming Concepts: List Comprehension
    http://en.wikibooks.org/wi­ki/Clojure_Programming/Con­cepts#List_Comprehension
  7. Clojure core API: for macro
    http://clojure.github.com/clo­jure/clojure.core-api.html#clojure.core/for
  8. cirrus machina – The Clojure for macro
    http://www.cirrusmachina.com/blog/com­ment/the-clojure-for-macro/
  9. Clojure.org: Clojure home page
    http://clojure.org/downloads
  10. Clojure.org: Vars and the Global Environment
    http://clojure.org/Vars
  11. Clojure.org: Refs and Transactions
    http://clojure.org/Refs
  12. Clojure.org: Atoms
    http://clojure.org/Atoms
  13. Clojure.org: Agents as Asynchronous Actions
    http://clojure.org/agents
  14. A Couple of Clojure Agent Examples
    http://lethain.com/a-couple-of-clojure-agent-examples/
  15. Clojure – Functional Programming for the JVM
    http://java.ociweb.com/mar­k/clojure/article.html
  16. Clojure quick reference
    http://faustus.webatu.com/clj-quick-ref.html
  17. 4Clojure
    http://www.4clojure.com/
  18. ClojureDoc
    http://clojuredocs.org/
  19. Clojure (Wikipedia EN)
    http://en.wikipedia.org/wiki/Clojure
  20. Clojure (Wikipedia CS)
    http://cs.wikipedia.org/wiki/Clojure
  21. Riastradh's Lisp Style Rules
    http://mumble.net/~campbe­ll/scheme/style.txt
  22. Dynamic Languages Strike Back
    http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html
  23. Scripting: Higher Level Programming for the 21st Century
    http://www.tcl.tk/doc/scripting.html
  24. Java Virtual Machine Support for Non-Java Languages
    http://docs.oracle.com/ja­vase/7/docs/technotes/gui­des/vm/multiple-language-support.html
  25. New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
    http://java.sun.com/develo­per/technicalArticles/Dyn­TypeLang/
  26. JSR 223: Scripting for the JavaTM Platform
    http://jcp.org/en/jsr/detail?id=223
  27. JSR 292: Supporting Dynamically Typed Languages on the JavaTM Platform
    http://jcp.org/en/jsr/detail?id=292
  28. Java 7: A complete invokedynamic example
    http://niklasschlimm.blog­spot.com/2012/02/java-7-complete-invokedynamic-example.html
  29. InvokeDynamic: Actually Useful?
    http://blog.headius.com/2007/01/in­vokedynamic-actually-useful.html
  30. A First Taste of InvokeDynamic
    http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html
  31. Java 6 try/finally compilation without jsr/ret
    http://cliffhacks.blogspot­.com/2008/02/java-6-tryfinally-compilation-without.html
  32. An empirical study of Java bytecode programs
    http://www.mendeley.com/research/an-empirical-study-of-java-bytecode-programs/
  33. Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
    http://www.mobilefish.com/tu­torials/java/java_quickgu­ide_jvm_instruction_set.html
  34. The JVM Instruction Set
    http://mpdeboer.home.xs4a­ll.nl/scriptie/node14.html
  35. Control Flow in the Java Virtual Machine
    http://www.artima.com/under­thehood/flowP.html
  36. Root.cz: Využití komprimovaných ukazatelů na objekty v JVM
    http://www.root.cz/clanky/vyuziti-komprimovanych-ukazatelu-na-objekty-v-nbsp-jvm/
  37. Root.cz: JamVM aneb alternativa k HotSpotu nejenom pro embedded zařízení a chytré telefony
    http://www.root.cz/clanky/jamvm-aneb-alternativa-k-hotspotu-nejenom-pro-embedded-zarizeni-tablety-a-chytre-telefony/
  38. The JavaTM Virtual Machine Specification, Second Edition
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/VMSpec­TOC.doc.html
  39. The class File Format
    http://java.sun.com/docs/bo­oks/jvms/second_edition/html/Clas­sFile.doc.html
  40. javap – The Java Class File Disassembler
    http://docs.oracle.com/ja­vase/1.4.2/docs/tooldocs/win­dows/javap.html
  41. javap-java-1.6.0-openjdk(1) – Linux man page
    http://linux.die.net/man/1/javap-java-1.6.0-openjdk
  42. Using javap
    http://www.idevelopment.in­fo/data/Programming/java/mis­cellaneous_java/Using_javap­.html
  43. Examine class files with the javap command
    http://www.techrepublic.com/ar­ticle/examine-class-files-with-the-javap-command/5815354
  44. BCEL Home page
    http://commons.apache.org/bcel/
  45. BCEL Manual
    http://commons.apache.org/bcel/ma­nual.html
  46. Byte Code Engineering Library (Wikipedia)
    http://en.wikipedia.org/wiki/BCEL
  47. Java programming dynamics, Part 7: Bytecode engineering with BCEL
    http://www.ibm.com/develo­perworks/java/library/j-dyn0414/
  48. Bytecode Engineering
    http://book.chinaunix.net/spe­cial/ebook/Core_Java2_Volu­me2AF/0131118269/ch13lev1sec6­.html
  49. BCEL Tutorial
    http://www.smfsupport.com/sup­port/java/bcel-tutorial!/
  50. ASM Home page
    http://asm.ow2.org/
  51. Seznam nástrojů využívajících projekt ASM
    http://asm.ow2.org/users.html
  52. ObjectWeb ASM (Wikipedia)
    http://en.wikipedia.org/wi­ki/ObjectWeb_ASM
  53. Java Bytecode BCEL vs ASM
    http://james.onegoodcooki­e.com/2005/10/26/java-bytecode-bcel-vs-asm/
  54. Bytecode Outline plugin for Eclipse (screenshoty + info)
    http://asm.ow2.org/eclipse/index.html
  55. aspectj (Eclipse)
    http://www.eclipse.org/aspectj/
  56. Aspect-oriented programming (Wikipedia)
    http://en.wikipedia.org/wi­ki/Aspect_oriented_program­ming
  57. AspectJ (Wikipedia)
    http://en.wikipedia.org/wiki/AspectJ
  58. EMMA: a free Java code coverage tool
    http://emma.sourceforge.net/
  59. Cobertura
    http://cobertura.sourceforge.net/
  60. FindBugs
    http://findbugs.sourceforge.net/
  61. GNU Classpath
    www.gnu.org/s/classpath/
  62. Java VMs Compared
    http://bugblogger.com/java-vms-compared-160/
  63. JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
    http://www.jcp.org/en/jsr/de­tail?id=223
  64. Scripting for the Java Platform
    http://java.sun.com/develo­per/technicalArticles/J2SE/Des­ktop/scripting/
  65. Scripting for the Java Platform (Wikipedia)
    http://en.wikipedia.org/wi­ki/Scripting_for_the_Java_Plat­form
  66. Java Community Process
    http://en.wikipedia.org/wi­ki/Java_Specification_Requ­est
  67. Java HotSpot VM Options
    http://www.oracle.com/technet­work/java/javase/tech/vmop­tions-jsp-140102.html
  68. Great Computer Language Shootout
    http://c2.com/cgi/wiki?Gre­atComputerLanguageShootout
  69. Java performance
    http://en.wikipedia.org/wi­ki/Java_performance
  70. Trying the prototype
    http://mail.openjdk.java.net/pi­permail/lambda-dev/2010-August/002179.html
  71. Better closures (for Java)
    http://blogs.sun.com/jrose/en­try/better_closures
  72. Lambdas in Java: An In-Depth Analysis
    http://www.infoq.com/articles/lambdas-java-analysis
  73. Class ReflectiveOperationException
    http://download.java.net/jdk7/doc­s/api/java/lang/Reflective­OperationException.html
  74. Scala Programming Language
    http://www.scala-lang.org/
  75. Run Scala in Apache Tomcat in 10 minutes
    http://www.softwaresecret­weapons.com/jspwiki/run-scala-in-apache-tomcat-in-10-minutes
  76. Fast Web Development With Scala
    http://chasethedevil.blog­spot.cz/2007/09/fast-web-development-with-scala.html
  77. Top five scripting languages on the JVM
    http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855
  78. Proposal: Indexing access syntax for Lists and Maps
    http://mail.openjdk.java.net/pi­permail/coin-dev/2009-March/001108.html
  79. Proposal: Elvis and Other Null-Safe Operators
    http://mail.openjdk.java.net/pi­permail/coin-dev/2009-March/000047.html
  80. Java 7 : Oracle pushes a first version of closures
    http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/
  81. Groovy: An agile dynamic language for the Java Platform
    http://groovy.codehaus.org/Operators
  82. Better Strategies for Null Handling in Java
    http://www.slideshare.net/Step­han.Schmidt/better-strategies-for-null-handling-in-java
  83. Control Flow in the Java Virtual Machine
    http://www.artima.com/under­thehood/flowP.html
  84. Java Virtual Machine
    http://en.wikipedia.org/wi­ki/Java_virtual_machine
  85. ==, .equals(), compareTo(), and compare()
    http://leepoint.net/notes-java/data/expressions/22com­pareobjects.html
  86. New JDK7 features
    http://openjdk.java.net/pro­jects/jdk7/features/
  87. Project Coin: Bringing it to a Close(able)
    http://blogs.sun.com/darcy/en­try/project_coin_bring_clo­se
  88. CloseableFinder source code
    http://blogs.sun.com/darcy/re­source/ProjectCoin/Closea­bleFinder.java
  89. Joe Darcy blog about JDK
    http://blogs.sun.com/darcy
  90. Java 7 – more dynamics
    http://www.baptiste-wicht.com/2010/04/java-7-more-dynamics/
  91. New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
    http://java.sun.com/develo­per/technicalArticles/Dyn­TypeLang/index.html

Autor článku

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