Programovací jazyk Clojure 10: kooperace mezi Clojure a Javou (pokračování)

14. 8. 2012
Doba čtení: 23 minut

Sdílet

Ilustrační obrázek
Autor: Depositphotos – stori
Ilustrační obrázek
V dnešní části seriálu o jazyce Java i o virtuálním stroji Javy budeme pokračovat v popisu komunikace mezi objekty a třídami napsanými v Javě a skripty, které jsou napsány v programovacím jazyce Clojure. Taktéž se budeme zabývat makrem import a vztahem mezi vestavěnými datovými typy Clojure a příslušnými typy v Javě.

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í

9. Odkazy na Internetu

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

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