Obsah
1. Vytváření javovských objektů s využitím speciální formy new
2. Volání metod s využitím speciální formy . (tečka)
3. Syntaktický cukr, který lze použít pro volání nestatických metod
4. Zkrácená forma volání statických (třídních) metod a čtení třídních atributů
5. Syntaktický cukr využitelný pro volání konstruktorů
6. Import tříd pomocí makra import
7. Vztah mezi vestavěnými typy jazyka Clojure a standardními třídami v Javě
8. Strukturované vestavěné typy jazyka Clojure a k nim korespondující rozhraní
1. Vytváření javovských objektů s využitím speciální formy new
V závěrečné kapitole předchozí části seriálu o virtuálním stroji Javy jsme si řekli základní informace o způsobu práce s javovskými třídami přímo v programovacím jazyce Clojure. Připomeňme si, že pro vytvoření instance libovolné třídy jazyka Java se používá speciální forma new, které se předá jméno třídy následované parametry, které jsou předány do konstruktoru třídy. Jméno třídy je symbol, který není dále vyhodnocován a podle dynamicky zjištěného počtu a typu parametrů se zavolá správný konstruktor, samozřejmě za předpokladu, že je vhodný konstruktor nalezen (tedy že existuje a současně má nastavena příslušná přístupová práva). Návratovou hodnotou speciální formy new je instance zvolené třídy, kterou je samozřejmě možné přiřadit k nějakému jménu s využitím def atd.
Pro zopakování se podívejme na několik jednoduchých demonstračních příkladů, v nichž se využívá speciální forma new:
; zavolání konstruktoru new java.lang.Boolean("true") user=> (new Boolean "true") true ; zavolání konstruktoru new java.lang.Integer(42) user=> (new Integer 42) 42 ; zavolání konstruktoru new java.lang.Integer("42") user=> (new Integer "42") 42 ; konstruktor bez parametrů user=> (new java.util.Date) #inst "2012-08-11T18:19:05.383-00:00" ; vytvoření prázdného seznamu s implicitně nastavenou kapacitou user=> (new java.util.ArrayList) #<ArrayList []> ; vytvoření prázdného seznamu s kapacitou nastavenou na 100 prvků user=> (new java.util.ArrayList 100) #<ArrayList []> ; vytvoření seznamu z již připravené kolekce ; (proč to funguje si vysvětlíme dále) user=> (new java.util.ArrayList '(1 2 3 4)) #<ArrayList [1, 2, 3, 4]> ; vytvoření seznamu z již připravené kolekce ; (proč to funguje si vysvětlíme dále) user=> (new java.util.ArrayList ["clojure" "a" "java"]) #<ArrayList [clojure, a, java]> user=>
Již na příkladu vytvoření instance třídy ArrayList bylo vidět, že Clojure skutečně dokáže vybrat správný konstruktor, což však obecně nemusí být vždy úplně jednoduché, protože mezi datovými typy využívanými v jazyce Java a v jazyce Clojure neexistuje jednoznačný vztah 1:1. Parametry předávané konstruktoru samozřejmě mohou být libovolného typu, což je názorně vidět na různých způsobech vytvoření instance třídy java.awt.Rectange používané v Javě například při práci s GUI:
; ~ new Rectangle() user=> (new java.awt.Rectangle) #<Rectangle java.awt.Rectangle[x=0,y=0,width=0,height=0]> ; ~ new Rectangle(int width, int height) user=> (new java.awt.Rectangle 320 240) #<Rectangle java.awt.Rectangle[x=0,y=0,width=320,height=240]> ; ~ new Rectangle(int x, int y, int width, int height) user=> (new java.awt.Rectangle 10 10 320 240) #<Rectangle java.awt.Rectangle[x=10,y=10,width=320,height=240]> ; ~ new Rectangle(Point p) user=> (new java.awt.Rectangle (new java.awt.Point 100 100)) #<Rectangle java.awt.Rectangle[x=100,y=100,width=0,height=0]> ; ~ Rectangle(Point p, Dimension d) user=> (new java.awt.Rectangle (new java.awt.Point 10 10) (new java.awt.Dimension 320 240)) #<Rectangle java.awt.Rectangle[x=10,y=10,width=320,height=240]> user=>
2. Volání metod s využitím speciální formy . (tečka)
Minule jsme si taktéž řekli, jakým způsobem je možné volat metody objektů, popř. statické metody tříd. K tomuto účelu se využívá speciální forma s názvem . (tečka). Prvním parametrem této speciální formy je buď název třídy, popř. instance objektu vrácená speciální formou new popsanou v předchozí kapitole. V případě, že prvním parametrem speciální formy . (tečka) je název třídy, je zavolána její statická metoda (zcela stejným způsobem jako v Javě). Pokud je prvním parametrem speciální formy . naopak instance objektu, zavolá se buď statická metoda nebo metoda objektu – v tomto případě se instance objektu předá jako první neviditelný parametr volané metody (opět stejně jako v Javě, kde se tento parametr skrývá pod klíčovým slovem this). V Clojure se nijak nerozlišuje mezi voláním metody s přístupem k atributu třídy či objektu, protože se v obou případech vrací nějaká hodnota. To znamená, že pomocí speciální formy . můžeme číst i atributy, ať již atributy konstantní, tak i atributy nastavované během „života“ objektu. Opět si ukažme několik jednoduchých demonstračních příkladů:
Přístup ke konstantním atributům tříd:
; maximální reprezentovatelná celočíselná hodnota ; kterou lze uložit do 32 bitů se znaménkem user=> (. Integer MAX_VALUE) 2147483647 ; minimální reprezentovatelná celočíselná hodnota ; kterou lze uložit do 32 bitů se znaménkem user=> (. Integer MIN_VALUE) -2147483648 ; velikost integeru v bitech user=> (. Integer SIZE) 32 ; primitivní typ, který obaluje třída Integer user=> (. Integer TYPE) int ; Ludolfovo číslo (resp. hodnota, která se mu přibližuje) user=> (. Math PI) 3.141592653589793 ; základ přirozených logaritmů user=> (. Math E) 2.718281828459045 ; číslo typu double, které se nejvíce přibližuje k nule user=> (. Double MIN_VALUE) 4.9E-324
Volání statických metod:
; v obalových třídách primitivních datových typů ; se nachází spousta užitečných metod user=> (. Integer toBinaryString 42) "101010" ; jeden ze způsobů výpočtu konstanty Pí user=> (* 4 (. Math atan 1)) 3.141592653589793 ; převod řetězce na číslo s využitím statické metody Integer.valueOf(String) user=> (. Integer valueOf "42") 42 ; změna číselné soustavy user=> (. Integer valueOf "42" 5) 22 ; při volání metody samozřejmě může nastat výjimka user=> (. Integer valueOf "ja nejsem cislo") NumberFormatException For input string: "ja nejsem cislo" java.lang.NumberFormatException.forInputString (NumberFormatException.java:48)
Volání metod objektů („nestatických metod“):
; vytvoření řetězce a zavolání metody String.length() user=> (. (new String "Hello world") length) 11 ; užitečná je automatická konverze mezi řetězci v Clojure a Javě ; (ve skutečnosti jde o stejný objektový typ) user=> (. "Hello world" length) 11 ; převod řetězce na velká písmena (vytvoří se NOVÝ řetězec) user=> (. "Hello world" toUpperCase) "HELLO WORLD" ; zavolání metody String.substring() user=> (. "Hello world" substring 0 5) "Hello" ; v tomto případě je však jednodušší použít vestavěnou ; funkci subs user=> (subs "Hello world" 0 5) "Hello"
3. Syntaktický cukr, který lze použít pro volání nestatických metod
Vzhledem k tomu, že tvůrci programovacího jazyka Clojure předpokládali, že se v programech budou velmi často používat třídy a objekty programovacího jazyka Java, byl do Clojure kromě speciálních forem new a . přidán navíc i „syntaktický cukr“, s jehož využitím je možné zapisovat konstruktory a volání nestatických metod (či čtení atributů objektů) poněkud čitelnějším způsobem, který si více podobá volání běžných funkcí Clojure (ve skutečnosti se však o plnohodnotné funkce nejedná – viz další text).
Namísto volání:
(. objekt metoda parametry_metody)
je možné alternativně použít tento „funkcionální“ způsob zápisu:
(.metoda objekt parametry_metody)
Výše uvedený alternativní způsob volání metod si ukážeme na dalších demonstračních příkladech:
; vytvoření instance třídy java.awt.Color ; a zavolání nestatické metody getRGB user=> (.getRGB (new java.awt.Color 0.0 1.0 0.5)) -16711808 ; odpovídá new String("Hello world").length() user=> (.length (new String "Hello world")) 11 ; efektivnější varianta user=> (.length "Hello world") 11 ; odpovídá new String("Hello world").toLowerCase() user=> (.toLowerCase "Hello world") "hello world"
Podobně je možné číst i nestatické atributy objektů:
user=> (.x (new java.awt.Rectangle 10 10 320 240)) 10
Nebo s využitím reference:
; vytvoření objektu a navázání na symbol "rect" user=> (def rect (new java.awt.Rectangle 10 10 320 240)) #'user/rect ; čtení atributu x user=> (.x rect) 10 ; čtení atributu y user=> (.y rect) 10 ; čtení atributu width user=> (.width rect) 320 ; čtení atributu height user=> (.height rect) 240 user=>
Výše uvedený alternativní způsob zápisu volání metody/čtení atributu vlastně odpovídá tomu, jak se nestatické metody volají ve skutečnosti i v Javě – v prvním (v Javě explicitně neuváděném) parametru je jim totiž skutečně předána reference na objekt, který je uvnitř metody přístupný přes klíčové slovo this. Tento způsob zápisu ovšem nelze použít pro volání statických metod či pro čtení statických atributů. To však neznamená, že by pro tento případ neexistoval příslušný „syntaktický cukr“. Ten je popsán v následující kapitole.
Použití alternativního způsobu zápisu si ukážeme na fragmentu programu, jenž po svém spuštění vytvoří okno, nastaví jeho základní parametry a posléze ho zobrazí na obrazovce. Tento fragment programu budeme v dalších kapitolách postupně upravovat a vylepšovat:
; vytvoření nového okna (def frame (new javax.swing.JFrame)) ; nastavení výchozí velikosti okna ; (zde se využívá syntaktický cukr pro zavolání metody objektu) (.setSize frame 400 300) ; nastavení akce zavolané při pokusu o zavření okna (.setDefaultCloseOperation frame (. javax.swing.WindowConstants DISPOSE_ON_CLOSE)) ; vše je připraveno, můžeme okno zobrazit (.setVisible frame true)
Tento způsob zápisu programů je v podstatě imperativní, což je však do značné míry zapříčiněno způsobem návrhu tříd v balíčku javax.swing, které k tomuto zápisu vedou.
4. Zkrácená forma volání statických (třídních) metod a čtení třídních atributů
V programovacím jazyce Clojure mají vývojáři k dispozici i další typ „syntaktického cukru“, který lze využít při volání statických metod i při čtení atributů třídy, tj. atributů, které jsou společné všem instancím třídy a existují i v případě, že žádná instance ještě vůbec není vytvořena. Zatímco v Javě i dalších objektově orientovaných jazycích většinou není ze syntaktického pohledu rozdíl mezi voláním statické a nestatické metody či mezi čtením statického a nestatického atributu, v Clojure je použit jiný přístup, který více odpovídá lispovskému způsobu zápisu forem, v němž je vždy prvním prvkem seznamu jméno funkce, speciální formy či makra a za ním následují případné parametry. Statické atributy se vždy jednoduše vyhodnotí na svou hodnotu, proto se při jejich zápisu používá syntaxe (jméno_třídy/jméno_atributu), přičemž mezi jménem třídy, lomítkem a jménem atributu nesmí být uvedeny žádné mezery, protože zápis (jméno_třídy / jméno_atributu) znamená zavolání metody jméno_třídy s dvojicí parametrů / a jméno_atributu, tedy něco zcela jiného:
; konstanta Pí user=> (Math/PI) 3.141592653589793 ; žádné mezery! user=> (Math / PI) RuntimeException Expecting var, but Math is mapped to class java.lang.Math cloj ure.lang.Util.runtimeException (Util.java:170) ; základ přirozených logaritmů user=> (Math/E) 2.718281828459045 ; maximální reprezentovatelná celočíselná hodnota ; kterou lze uložit do 32 bitů se znaménkem user=> (Integer/MAX_VALUE) 2147483647 ; minimální reprezentovatelná celočíselná hodnota ; kterou lze uložit do 32 bitů se znaménkem user=> (Integer/MIN_VALUE) -2147483648 ; velikost integeru v bitech user=> (Integer/SIZE) 32 ; primitivní typ, který obaluje třída Integer user=> (Integer/TYPE) int ; číslo typu double, které se nejvíce přibližuje k nule user=> (Double/MIN_VALUE) 4.9E-324 ; konstanta reprezentující zelenou barvu user=> (java.awt.Color/GREEN) #<Color java.awt.Color[r=0,g=255,b=0]>
Podobným způsobem je možné zavolat libovolnou statickou metodu a případně jí předat i nějaké parametry:
; převod celého čísla na řetězec ; s jeho binární reprezentací user=> (Integer/toBinaryString 42) "101010" ; převod řetězce na číslo user=> (Integer/parseInt "42") 42 ; jeden ze způsobů výpočtu konstanty Pí user=> (* 4 (Math/atan 1)) 3.141592653589793
Demonstrační příklad uvedený na konci předchozí kapitoly je možné s využitím syntaktického cukru (název_třídy/název_atributu) zkrátit následujícím způsobem:
; vytvoření nového okna (def frame (new javax.swing.JFrame)) ; nastavení výchozí velikosti okna ; (zde se využívá syntaktický cukr pro zavolání metody objektu) (.setSize frame 400 300) ; nastavení akce zavolané při pokusu o zavření okna ; (zde se využívá syntaktický cukr pro přístup ke statickému atributu) (.setDefaultCloseOperation frame javax.swing.WindowConstants/DISPOSE_ON_CLOSE) ; vše je připraveno, můžeme okno zobrazit (.setVisible frame true)
5. Syntaktický cukr využitelný pro volání konstruktorů
Poslední formou „syntaktického cukru“, s níž se v dnešním článku seznámíme, je zjednodušené volání konstruktorů javovských tříd. Již v první kapitole jsme si řekli, že konstruktor se volá s využitím speciální formy new následujícím způsobem:
; ~ new Rectangle() user=> (new java.awt.Rectangle) #<Rectangle java.awt.Rectangle[x=0,y=0,width=0,height=0]> ; ~ new Rectangle(int width, int height) user=> (new java.awt.Rectangle 320 240) #<Rectangle java.awt.Rectangle[x=0,y=0,width=320,height=240]> ; ~ new Rectangle(int x, int y, int width, int height) user=> (new java.awt.Rectangle 10 10 320 240) #<Rectangle java.awt.Rectangle[x=10,y=10,width=320,height=240]> ; ~ new Rectangle(Point p) user=> (new java.awt.Rectangle (new java.awt.Point 100 100)) #<Rectangle java.awt.Rectangle[x=100,y=100,width=0,height=0]> ; ~ Rectangle(Point p, Dimension d) user=> (new java.awt.Rectangle (new java.awt.Point 10 10) (new java.awt.Dimension 320 240)) #<Rectangle java.awt.Rectangle[x=10,y=10,width=320,height=240]>
To je ovšem příliš nepřehledné a proto je v programovacím jazyku Clojure možné namísto speciální formy new jednoduše napsat jméno třídy, za nímž je napsána tečka, a to bez jakéhokoli oddělovače (to je zde velmi důležité). Pokud má konstruktor nějaké parametry, zapisují se až za touto tečkou, samozřejmě již normálně oddělené mezerou nebo jiným bílým znakem (znaky). Z pohledu programátora je tedy konstruktor jen další běžnou funkcí s trošku neobvyklým jménem, jehož návratovou hodnotou je instance třídy. Předchozích pět konstruktorů by tedy bylo možné zapsat i následujícím zkráceným způsobem:
; ~ new Rectangle() user=> (java.awt.Rectangle.) #<Rectangle java.awt.Rectangle[x=0,y=0,width=0,height=0]> ; ~ new Rectangle(int width, int height) user=> (java.awt.Rectangle. 320 240) #<Rectangle java.awt.Rectangle[x=0,y=0,width=320,height=240]> ; ~ new Rectangle(int x, int y, int width, int height) user=> (java.awt.Rectangle. 10 10 320 240) #<Rectangle java.awt.Rectangle[x=10,y=10,width=320,height=240]> ; ~ new Rectangle(Point p) user=> (java.awt.Rectangle. (java.awt.Point. 100 100)) #<Rectangle java.awt.Rectangle[x=100,y=100,width=0,height=0]> ; ~ Rectangle(Point p, Dimension d) user=> (java.awt.Rectangle. (java.awt.Point. 100 100) (java.awt.Dimension. 320 240)) #<Rectangle java.awt.Rectangle[x=100,y=100,width=320,height=240]> user=>
Opět se vraťme k fragmentu programu pro vytvoření a zobrazení okna. Ten lze přepsat tak, aby využíval alternativní způsob zápisu konstruktorů následujícím způsobem:
; vytvoření nového okna ; (zde se používá syntaktický cukr pro zavolání konstruktoru) (def frame (javax.swing.JFrame.)) ; nastavení výchozí velikosti okna ; (zde se využívá syntaktický cukr pro zavolání metody objektu) (.setSize frame 400 300) ; nastavení akce zavolané při pokusu o zavření okna ; (zde se využívá syntaktický cukr pro přístup ke statickému atributu) (.setDefaultCloseOperation frame javax.swing.WindowConstants/DISPOSE_ON_CLOSE) ; vše je připraveno, můžeme okno zobrazit (.setVisible frame true)
Popř. můžeme přímo nastavit i titulek okna:
; vytvoření nového okna s titulkem (def frame (javax.swing.JFrame. "Testovací okno))
6. Import tříd pomocí makra import
Při čtení předchozích demonstračních příkladů si pozorný čtenář pravděpodobně říkal, že zápis celého jména třídy i s příslušným jménem balíčku je zbytečně zdlouhavý a vede k tvorbě nepřehledných programů. To je skutečně pravda, ovšem v Clojure je možné pro zkrácení zápisu použít makro import, které provede následující operaci: vytvoří nové mapování mezi prostým jménem třídy a úplným jménem třídy (tj. jménem balíčku + názvem třídy). Zdánlivě se jedná o stejnou funkci, kterou provádí direktiva import v programovacím jazyku Java, ovšem zatímco v Javě je tato direktiva použita pouze v čase překladu (nemá prakticky žádný vliv na generovaný bajtkód), v případě jazyka Clojure je mapování provedeno v době běhu programu. Makro import se používá následujícím způsobem:
(import (jméno_balíčku seznam_jmen_tříd))
Příklad použití:
; import trojice tříd z balíčku java.util user=> (import (java.util List ArrayList Date)) java.util.Date ; zavolání bezparametrického konstruktoru třídy Date user=> (Date.) #inst "2012-08-12T10:29:42.262-00:00" user=>
Potíže nastanou ve chvíli, kdy dojde k importu stejně pojmenovaných tříd náležejících do různých balíčků. V těchto případech je nejjednodušší prostě import jedné z těchto tříd neprovádět a namísto toho uvádět celý název třídy (což ostatně bude případné čtenáře zdrojového kódu méně matoucí):
; import třídy java.util.Date user=> (import (java.util Date)) java.util.Date ; pokus o import třídy java.sql.Date user=> (import (java.sql Date)) IllegalStateException Date already refers to: class java.util.Date in namespace: user clojure.lang.Namespace.referenceClass (Namespace.java:140) user=>
Již naposledy se podívejme na náš demonstrační příklad s vytvořením a následným zobrazením okna. Jeho konečná podoba může s využitím makra import vypadat následovně:
; import dvou tříd JFrame a WindowConstants z balíčku javax.swing user=> (import (javax.swing JFrame WindowConstants)) javax.swing.WindowConstants ; vytvoření nového okna - nemusíme uvádět plné jméno třídy ; (zde se používá syntaktický cukr pro zavolání konstruktoru) user=> (def frame (JFrame.)) #'user/frame ; nastavení výchozí velikosti okna ; (zde se využívá syntaktický cukr pro zavolání metody objektu) user=> (.setSize frame 400 300) nil ; nastavení akce zavolané při pokusu o zavření okna ; - nemusíme uvádět plné jméno třídy s konstantou DISPOSE_ON_CLOSE ; (zde se využívá syntaktický cukr pro přístup ke statickému atributu) user=> (.setDefaultCloseOperation frame WindowConstants/DISPOSE_ON_CLOSE) nil ; vše je připraveno, můžeme okno zobrazit user=> (.setVisible frame true) nil user=>
Stále se jedná o až nepříjemně imperativní kód, tomu se však při práci s GUI, které je „stavové“, pravděpodobně jen těžko ubráníme :-) Ostatně není bez zajímavosti, že právě v GUI lze asi nejlépe využít principy OOP, na rozdíl od některých jiných oblastí informatiky.
7. Vztah mezi vestavěnými typy jazyka Clojure a standardními třídami v Javě
Při komunikaci mezi částí programu napsanou v programovacím jazyku Java a jinou částí naprogramovanou s využitím Clojure je samozřejmě nutné mezi oběma částmi předávat nějaká data (typicky se jedná o parametry volaných metod). Problém nastává v tom, jakého typu tato data mají být. Zdaleka se nejedná jen o problém programovacího jazyka Clojure, ale jakéhokoli jiného dynamicky typovaného jazyka, který má komunikovat s jazykem staticky typovaným. Pro příklad nemusíme chodit daleko, stačí se podívat na různé chování standardního interpretru jazyka JavaScript, který je součástí JDK6 i JDK7.
Asi nejjednodušší je převod primitivních datových typů, resp. instancí jejich obalových tříd, do skriptů psaných v Clojure. Základní mapování mezi jednoduchými typy Clojure a příslušnou třídou či rozhraním v Javě je vypsáno v následující tabulce:
Typ | Literál v Clojure | Java třída/rozhraní |
---|---|---|
Boolean | true | java.lang.Boolean |
Number | 42 | java.lang.Number |
String | „string“ | java.lang.String |
Character | \x | java.lang.Character |
Keyword | :klíč | clojure.lang.Keyword |
V praxi samozřejmě není možné vytvořit přímo instanci rozhraní java.lang.Number, ale musí se použít instance některé konkrétní obalové třídy, například java.lang.Long či java.lang.Double. Vyzkoušejme si tedy, jak se jednoduché datové typy převádí z Clojure do Javy – nesmíme totiž zapomenout na to, že skripty v Clojure jsou kompilovány do zcela běžného javovského bajtkódu, tudíž se v něm musí používat buď primitivní datové typy Javy nebo vhodné objektové typy, popř. obalové třídy nad primitivními datovými typy.
U nečíselných datových typů je situace jednoduchá:
user=> (.getClass true) java.lang.Boolean user=> (.getClass "Hello world!") java.lang.String user=> (.getClass \x) java.lang.Character user=> (.getClass :klicove-slovo) clojure.lang.Keyword
V případě číselných datových typů se většinou (ale ne vždy) u celých čísel používá primitivní datový typ long s jeho obalovou třídou java.lang.Long a u čísel s desetinnou tečkou pak primitivní datový typ double s obalovou třídou java.lang.Double. Ovšem u zlomků je situace odlišná, podobně jako u hodnoty typu java.lang.BigDecimal apod.:
user=> (.getClass 42) java.lang.Long user=> (.getClass 16RFF) java.lang.Long user=> (.getClass 42.0) java.lang.Double user=> (.getClass 1/2) clojure.lang.Ratio user=> (.getClass 42M) java.math.BigDecimal
V praxi to díky automatickému boxingu a unboxingu znamená, že můžeme velmi snadno zavolat jakoukoli javovskou metodu, která jako svůj parametr či parametry očekává číselnou hodnotu a v případě hodnot typu double lze pracovat i s nekonečnem či speciální hodnotou NaN:
user=> (Math/log 0) -Infinity user=> (Math/log 10) 2.302585092994046 user=> (Math/exp 100) 2.6881171418161356E43 user=> (Math/exp 1000) Infinity user=> (Math/sin (Math/exp 1000)) NaN
8. Strukturované vestavěné typy jazyka Clojure a k nim korespondující rozhraní
Vzhledem k tomu, že v Clojure se většina složitějších datových struktur vytváří s využitím seznamů, vektorů, map a množin, bude zajímavé zjistit, jakým způsobem se tyto datové typy mapují na rozhraní v Javě. Pravděpodobně (a to zcela správně) očekáváte, že seznamy, vektory a množiny budou implementovat rozhraní java.util.Collection a v případě map bude implementováno rozhraní java.util.Map. Navíc všechny čtyři zmíněné datové typy implementují i rozhraní java.util.Iterable a v případě vektorů i java.util.Comparable:
Rozhraní | seznam | vektor | mapa | množina |
---|---|---|---|---|
java.util.Collection | ano | ano | ne | ano |
java.util.List | ano | ano | ne | ne |
java.util.Map | ne | ne | ano | ne |
java.util.Set | ne | ne | ne | ano |
java.util.RandomAccess | ne | ano | ne | ne |
java.lang.Iterable | ano | ano | ano | ano |
java.lang.Comparable | ne | ano | ne | ne |
Co to však znamená v praxi? Podívejme se na následující příklad, v němž je vytvořena instance třídy java.util.TreeSet, jejímuž konstruktoru (tečka na konci názvu třídy) je předán vektor, tedy nativní strukturovaný datový typ jazyka Clojure. Následně je do množiny přidán další vektor:
user=> (def mnozina (java.util.TreeSet. [10 20 1 2 0])) #'user/mnozina user=> (.addAll mnozina [0 1 2 3 4]) true user=> (.toString mnozina) "[0, 1, 2, 3, 4, 10, 20]" user=>
Výše uvedený příklad byl naprosto korektní, protože ve třídě java.util.TreeSet existuje jak konstruktor:
TreeSet(Collection<? extends E> c)
tak i metoda:
addAll(Collection<? extends E> c)
Generikami se v tomto případě vůbec nemusíme zabývat, protože informace o nich se stejně v bajtkódu nepoužívají a programy v Clojure jsou překládány do bajtkódu, tudíž se volá konstruktor:
TreeSet(Collection c)
a metoda:
addAdd(Collection c)
Podívejme se nyní na složitější příklad, kde je využita funkce range vracející taktéž kolekci:
; konstrukce množiny + její inicializace user=> (def mnozina (java.util.HashSet. (range 1 10))) #'user/mnozina ; přidání dalších prvků do množiny user=> (.addAll mnozina (range 20 30)) true ; výpis aktuálního obsahu množiny user=> (.toString mnozina) "[1, 2, 3, 4, 5, 6, 7, 8, 9, 21, 20, 23, 22, 25, 24, 27, 26, 29, 28]" ; odstranění vybraných prvků 15..25 (jen těch, které existují) user=> (.removeAll mnozina (range 15 25)) true ; výpis aktuálního obsahu množiny user=> (.toString mnozina) "[1, 2, 3, 4, 5, 6, 7, 8, 9, 25, 27, 26, 29, 28]" user=>
Musíme si však dát dobrý pozor na to, že všechny čtyři strukturované datové typy Clojure jsou neměnitelné (immutable), takže při pokusu o modifikaci obsahu nějaké struktury dojde k výjimce:
user=> (java.util.Collections/sort '(1 2 3 4)) UnsupportedOperationException java.util.Collections$UnmodifiableList$1.set (Co llections.java:1186) user=> (java.util.Collections/sort [1 2 3 4]) UnsupportedOperationException clojure.lang.APersistentVector$1.set (APersisten tVector.java:231)
9. Odkazy na Internetu
- Clojure.org: Clojure home page
http://clojure.org/downloads - Clojure.org: Vars and the Global Environment
http://clojure.org/Vars - Clojure.org: Refs and Transactions
http://clojure.org/Refs - Clojure.org: Atoms
http://clojure.org/Atoms - Clojure.org: Agents as Asynchronous Actions
http://clojure.org/agents - A Couple of Clojure Agent Examples
http://lethain.com/a-couple-of-clojure-agent-examples/ - Clojure – Functional Programming for the JVM
http://java.ociweb.com/mark/clojure/article.html - Clojure quick reference
http://faustus.webatu.com/clj-quick-ref.html - 4Clojure
http://www.4clojure.com/ - ClojureDoc
http://clojuredocs.org/ - Clojure (Wikipedia EN)
http://en.wikipedia.org/wiki/Clojure - Clojure (Wikipedia CS)
http://cs.wikipedia.org/wiki/Clojure - Riastradh's Lisp Style Rules
http://mumble.net/~campbell/scheme/style.txt - Dynamic Languages Strike Back
http://steve-yegge.blogspot.cz/2008/05/dynamic-languages-strike-back.html - Scripting: Higher Level Programming for the 21st Century
http://www.tcl.tk/doc/scripting.html - Java Virtual Machine Support for Non-Java Languages
http://docs.oracle.com/javase/7/docs/technotes/guides/vm/multiple-language-support.html - New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
http://java.sun.com/developer/technicalArticles/DynTypeLang/ - JSR 223: Scripting for the JavaTM Platform
http://jcp.org/en/jsr/detail?id=223 - JSR 292: Supporting Dynamically Typed Languages on the JavaTM Platform
http://jcp.org/en/jsr/detail?id=292 - Java 7: A complete invokedynamic example
http://niklasschlimm.blogspot.com/2012/02/java-7-complete-invokedynamic-example.html - InvokeDynamic: Actually Useful?
http://blog.headius.com/2007/01/invokedynamic-actually-useful.html - A First Taste of InvokeDynamic
http://blog.headius.com/2008/09/first-taste-of-invokedynamic.html - Java 6 try/finally compilation without jsr/ret
http://cliffhacks.blogspot.com/2008/02/java-6-tryfinally-compilation-without.html - An empirical study of Java bytecode programs
http://www.mendeley.com/research/an-empirical-study-of-java-bytecode-programs/ - Java quick guide: JVM Instruction Set (tabulka všech instrukcí JVM)
http://www.mobilefish.com/tutorials/java/java_quickguide_jvm_instruction_set.html - The JVM Instruction Set
http://mpdeboer.home.xs4all.nl/scriptie/node14.html - Control Flow in the Java Virtual Machine
http://www.artima.com/underthehood/flowP.html - Root.cz: Využití komprimovaných ukazatelů na objekty v JVM
http://www.root.cz/clanky/vyuziti-komprimovanych-ukazatelu-na-objekty-v-nbsp-jvm/ - 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/ - The JavaTM Virtual Machine Specification, Second Edition
http://java.sun.com/docs/books/jvms/second_edition/html/VMSpecTOC.doc.html - The class File Format
http://java.sun.com/docs/books/jvms/second_edition/html/ClassFile.doc.html - javap – The Java Class File Disassembler
http://docs.oracle.com/javase/1.4.2/docs/tooldocs/windows/javap.html - javap-java-1.6.0-openjdk(1) – Linux man page
http://linux.die.net/man/1/javap-java-1.6.0-openjdk - Using javap
http://www.idevelopment.info/data/Programming/java/miscellaneous_java/Using_javap.html - Examine class files with the javap command
http://www.techrepublic.com/article/examine-class-files-with-the-javap-command/5815354 - BCEL Home page
http://commons.apache.org/bcel/ - BCEL Manual
http://commons.apache.org/bcel/manual.html - Byte Code Engineering Library (Wikipedia)
http://en.wikipedia.org/wiki/BCEL - Java programming dynamics, Part 7: Bytecode engineering with BCEL
http://www.ibm.com/developerworks/java/library/j-dyn0414/ - Bytecode Engineering
http://book.chinaunix.net/special/ebook/Core_Java2_Volume2AF/0131118269/ch13lev1sec6.html - BCEL Tutorial
http://www.smfsupport.com/support/java/bcel-tutorial!/ - ASM Home page
http://asm.ow2.org/ - Seznam nástrojů využívajících projekt ASM
http://asm.ow2.org/users.html - ObjectWeb ASM (Wikipedia)
http://en.wikipedia.org/wiki/ObjectWeb_ASM - Java Bytecode BCEL vs ASM
http://james.onegoodcookie.com/2005/10/26/java-bytecode-bcel-vs-asm/ - Bytecode Outline plugin for Eclipse (screenshoty + info)
http://asm.ow2.org/eclipse/index.html - aspectj (Eclipse)
http://www.eclipse.org/aspectj/ - Aspect-oriented programming (Wikipedia)
http://en.wikipedia.org/wiki/Aspect_oriented_programming - AspectJ (Wikipedia)
http://en.wikipedia.org/wiki/AspectJ - EMMA: a free Java code coverage tool
http://emma.sourceforge.net/ - Cobertura
http://cobertura.sourceforge.net/ - FindBugs
http://findbugs.sourceforge.net/ - GNU Classpath
www.gnu.org/s/classpath/ - Java VMs Compared
http://bugblogger.com/java-vms-compared-160/ - JSRs: Java Specification Requests – JSR 223: Scripting for the Java Platform
http://www.jcp.org/en/jsr/detail?id=223 - Scripting for the Java Platform
http://java.sun.com/developer/technicalArticles/J2SE/Desktop/scripting/ - Scripting for the Java Platform (Wikipedia)
http://en.wikipedia.org/wiki/Scripting_for_the_Java_Platform - Java Community Process
http://en.wikipedia.org/wiki/Java_Specification_Request - Java HotSpot VM Options
http://www.oracle.com/technetwork/java/javase/tech/vmoptions-jsp-140102.html - Great Computer Language Shootout
http://c2.com/cgi/wiki?GreatComputerLanguageShootout - Java performance
http://en.wikipedia.org/wiki/Java_performance - Trying the prototype
http://mail.openjdk.java.net/pipermail/lambda-dev/2010-August/002179.html - Better closures (for Java)
http://blogs.sun.com/jrose/entry/better_closures - Lambdas in Java: An In-Depth Analysis
http://www.infoq.com/articles/lambdas-java-analysis - Class ReflectiveOperationException
http://download.java.net/jdk7/docs/api/java/lang/ReflectiveOperationException.html - Scala Programming Language
http://www.scala-lang.org/ - Run Scala in Apache Tomcat in 10 minutes
http://www.softwaresecretweapons.com/jspwiki/run-scala-in-apache-tomcat-in-10-minutes - Fast Web Development With Scala
http://chasethedevil.blogspot.cz/2007/09/fast-web-development-with-scala.html - Top five scripting languages on the JVM
http://www.infoworld.com/d/developer-world/top-five-scripting-languages-the-jvm-855 - Proposal: Indexing access syntax for Lists and Maps
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/001108.html - Proposal: Elvis and Other Null-Safe Operators
http://mail.openjdk.java.net/pipermail/coin-dev/2009-March/000047.html - Java 7 : Oracle pushes a first version of closures
http://www.baptiste-wicht.com/2010/05/oracle-pushes-a-first-version-of-closures/ - Groovy: An agile dynamic language for the Java Platform
http://groovy.codehaus.org/Operators - Better Strategies for Null Handling in Java
http://www.slideshare.net/Stephan.Schmidt/better-strategies-for-null-handling-in-java - Control Flow in the Java Virtual Machine
http://www.artima.com/underthehood/flowP.html - Java Virtual Machine
http://en.wikipedia.org/wiki/Java_virtual_machine - ==, .equals(), compareTo(), and compare()
http://leepoint.net/notes-java/data/expressions/22compareobjects.html - New JDK7 features
http://openjdk.java.net/projects/jdk7/features/ - Project Coin: Bringing it to a Close(able)
http://blogs.sun.com/darcy/entry/project_coin_bring_close - CloseableFinder source code
http://blogs.sun.com/darcy/resource/ProjectCoin/CloseableFinder.java - Joe Darcy blog about JDK
http://blogs.sun.com/darcy - Java 7 – more dynamics
http://www.baptiste-wicht.com/2010/04/java-7-more-dynamics/ - New JDK 7 Feature: Support for Dynamically Typed Languages in the Java Virtual Machine
http://java.sun.com/developer/technicalArticles/DynTypeLang/index.html