Top Banner
Univerzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu dne otevřených dveří na FIM Diplomová práce Autor: Bc. David Richter Studijní obor: Aplikovaná informatika - 2 Vedoucí práce: doc. Ing. Filip Malý, Ph.D. Hradec Králové Duben 2016
62

Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

Jul 12, 2019

Download

Documents

lynhu
Welcome message from author
This document is posted to help you gain knowledge. Please leave a comment to let me know what you think about it! Share it to your friends and learn new things together.
Transcript
Page 1: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

Univerzita Hradec Králové

Fakulta informatiky a managementu

Katedra informatiky a kvantitativních metod

Aplikace pro podporu dne otevřených dveří na FIM

Diplomová práce

Autor: Bc. David Richter Studijní obor: Aplikovaná informatika - 2

Vedoucí práce: doc. Ing. Filip Malý, Ph.D.

Hradec Králové Duben 2016

Page 2: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

Prohlášení:

Prohlašuji, že jsem diplomovou práci zpracoval samostatně a s použitím

uvedené literatury.

V Hradci Králové dne 26.4.2016 Bc. David Richter

Page 3: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

Poděkování:

Děkuji vedoucímu diplomové práce doc. Ing. Filipovi Malému, Ph.D. za

metodické vedení práce a pomoc se zpracováním.

Page 4: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu
Page 5: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

Anotace

V této práci je popsán programovací jazyk Kotlin a jeho použití pro vytvoření

aplikace pro podporu dne otevřených dveří na FIM. V první části je vysvětlena

syntaxe Kotlinu s ukázkami zdrojového kódu včetně popisu dané ukázky. Dále jsou

popsány některé pokročilejší funkce Kotlinu, část standardní knihovny a porovnání

Kotlinu a Javy ve formě kompilace do bytekódu a dekompilace do Javy. V další části

je uveden popis funkcionality aplikace pro podporu dne otevřených dveří. Nejdříve

je popsán model aplikace, kde je uvedena struktura, části aplikace včetně vysvětlení

jejich funkcí, a poté je popsána implementace aplikace a použitých frameworků,

uvedení jejich základní funkčnosti, a ukázky částí zdrojového kódu aplikace.

Annotation

Title: Application to support the Open days at FIM

The subject of this thesis is the Kotlin programming language and its use in creating

an application to support the Open days at FIM. In the first part, Kotlin’s syntax is

explained and source code examples are given and analyzed. In addition, selected

advanced functions of the Kotlin language are examined, in particular part of the

standard library. This is followed by a comparison of the Kotlin and Java

programming languages made in the form of compilation to bytecode and

decompilation to Java. The subsequent part of the thesis focuses on the application’s

functionality. Firstly, a model of the application is introduced. This includes

a description of the application model’s overall structure and a more detailed

explanation of its individual parts. Secondly, the application’s implementation is

discussed in relation to the used frameworks and their basic description. The thesis

is concluded by providing sample parts of the application’s source code.

Page 6: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

Obsah

1 Úvod................................................................................................................................................. 1

2 Kotlin ............................................................................................................................................... 3

2.1 Základní prvky syntaxe .................................................................................................... 3

2.1.1 Třídy................................................................................................................................ 3

2.1.2 Konstruktory ............................................................................................................... 4

2.1.3 Funkce ............................................................................................................................ 5

2.1.4 Proměnné...................................................................................................................... 6

2.1.5 Řízení běhu programu ............................................................................................. 7

2.1.6 Modifikátory viditelnosti ........................................................................................ 9

2.2 Pokročilé vlastnosti .........................................................................................................10

2.2.1 Null reference ............................................................................................................10

2.2.2 Data třídy ....................................................................................................................12

2.2.3 Generické typy ..........................................................................................................12

2.2.4 Lambda výraz ............................................................................................................14

2.2.5 Delegates .....................................................................................................................15

2.3 Standardní knihovna .......................................................................................................16

2.3.1 Kolekce .........................................................................................................................16

2.3.2 Obecné funkce ...........................................................................................................18

2.4 Dekompilace do Javy .......................................................................................................22

2.4.1 Top level funkce .......................................................................................................22

2.4.2 Lambda výrazy..........................................................................................................23

2.4.3 Přiřazení konstruktu pro řízení běhu programu .........................................25

2.4.4 Defaultní hodnoty parametrů .............................................................................26

3 Aplikace pro podporu dne otevřených dveří .................................................................28

3.1 Model .....................................................................................................................................28

Page 7: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

3.1.1 Serverová část ...........................................................................................................28

3.1.2 Mobilní část ................................................................................................................28

3.2 Implementace ....................................................................................................................29

3.2.1 Serverová část ...........................................................................................................29

3.2.2 Mobilní část ................................................................................................................37

4 Shrnutí výsledků .......................................................................................................................47

5 Závěry a doporučení................................................................................................................48

6 Seznam použité literatury .....................................................................................................50

7 Přílohy ..........................................................................................................................................52

Page 8: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

Seznam obrázků

Obr. 1 Stránka pro úpravu informací. ........................................................................................36

Obr. 2 Stránka pro úpravu událostí. ...........................................................................................37

Obr. 3 Hlavní aktivita a detail události. .....................................................................................46

Seznam tabulek

Tabulka 1 modifikátory viditelnosti uvnitř třídy. .................................................................10

Tabulka 2 modifikátory viditelnosti v rámci balíčku. ..........................................................10

Page 9: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

1

1 Úvod

Tato práce se zabývá programovacím jazykem Kotlin, konkrétně verzí 1.0, jeho

syntaxí, funkcemi a srovnáním s programovacím jazykem Java, se kterým je

kompatibilní. A také praktickým využitím jazyka Kotlin pro implementaci aplikace

pro podporu dne otevřených dveří na Fakultě informatiky a managementu,

Univerzity Hradec Králové.

Cílem této práce je představit jazyk Kotlin a jeho praktické využití při vývoji reálné

aplikace, se zaměřením na porovnání s programovacím jazykem Java. Zabývá se

rozdíly mezi těmito jazyky a úskalími, které mohou vzniknout při přechodu z Javy.

Popis jazyka Kotlin čerpá z dokumentace výrobce na oficiálních stránkách jazyka,

kde je také možné najít další rozšiřující informace a aktuální novinky o vývoji tohoto

jazyka, včetně plánovaných nových funkcí. Existuje zde také možnost se zapojit do

diskuze a ovlivnit tak budoucí funkce jazyka.

V první části práce je popsán jazyk Kotlin, jeho syntaxe a funkce s krátkými příklady

jeho použití, včetně podrobného vysvětlení daných funkcí, například pomocí

referencí na funkcionalitu v Javě.

Nejdříve jsou popsány základní syntaktické prvky Kotlinu, jako je zápis tříd, funkcí

a proměnných. Dále jsou popsány konstrukty pro řízení běhu programu, včetně

srovnání s jejich obdobami v Javě a jaké funkce mají v Kotlinu navíc.

V další části jsou uvedeny pokročilé funkce a vlastnosti jazyka Kotlin, například

speciální syntaxe pro typy, které mohou obsahovat null referenci. Data třídy, které

zkracují zápis POJO tříd a generují implementaci některých funkcí. Generické typy,

jejich specifika a rozdíly oproti Javě. Lambda výrazy a jejich možnosti použití.

Také se zde nachází popis některých užitečných tříd ze standardní knihovny Kotlinu

a popis, jak mohou být použity pro ulehčení řešení často opakovaných vzorů při

programování. V této kapitole jsou popsány kolekce a jejich specifika při použití

v Kotlinu a také některé funkce, které lze použít pro práci s různými objekty

i některé více specializované používané například pro operace se soubory.

V další kapitole je ukázka jak vypadá Kotlin po kompilaci a zpětné dekompilaci do

Javy, pro lepší pochopení, jak fungují některé funkce Kotlinu, například lambda

Page 10: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

2

výrazy, i když jsou zkompilovány pomocí Javy verze 1.6, která nemá jejich přímou

podporu.

V druhé části je popsána aplikace pro podporu dne otevřených dveří na FIM. Nejprve

jsou rozebrány její funkce a její přínos. V další kapitole je popsán model, zejména že

je aplikace rozdělena na serverovou a mobilní část, a popis jak spolu vzájemně

komunikují a další informace o designu chování aplikace.

A v poslední části se nachází konkrétní popis implementace obou částí, včetně

použitých frameworků, stručného popisu jejich funkce a jak jsou použity.

Page 11: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

3

2 Kotlin

Kotlin je staticky typovaný programovací jazyk určený pro Java Virtual Machine

(JVM) a JavaScript. Kotlin je vyvíjen společností JetBrains s.r.o., která vyvinula mimo

jiné vývojové prostředí pro programovací jazyk Java - IntelliJ IDEA a používá Kotlin

pro vývoj plug-inů a nových produktů.

Protože mohou být zdrojové soubory kompilovány do bytekódu, je tento jazyk

kompatibilní s knihovnami pro programovací jazyk Java a ostatní JVM jazyky, jako

je například Groovy.

2.1 Základní prvky syntaxe

Syntaxe Kotlinu je mezistupněm mezi Groovy a Javou. Groovy jako dynamický jazyk

umožňuje velmi rozsáhlou škálu použití, ale na úkor typové bezpečnosti při

kompilaci – kompilátor nemůže odhalit, zda daná funkce nebo třída nebude

dostupná za běhu. Naproti tomu Kotlin je statický jazyk, takže zajišťuje stejnou

typovou bezpečnost při kompilaci jako Java, ale zároveň disponuje už v základu

některými pokročilými konstrukty, které v Javě chybí, například přetěžování

operátorů, rozšířenou podporu pro takzvané Ranges nebo pokročilejšími

konstrukty pro řízení běhu programu – if, switch, atd. Kotlin se také soustředí na

některé neduhy Javy, například: null safety nebo checked exceptions. Mnoho funkcí

jazyka je přizpůsobeno radám uvedeným v knize Effective Java. [1]

Kotlin stejně jako v Java používá pro zapouzdření souvisejícího kódu balíčky

(package) a třídy. Na rozdíl od Javy umožňuje Kotlin mít funkce i mimo třídu. Jednou

z výhod oproti Javě je stručnější zápis kódu, tzn., že pro napsání stejné funkčnosti

nevyžaduje Kotlin takové množství nadbytečného kódu. Další výhodu lze spatřit

v tom, že v Kotlinu není nutné ukončovat příkazy středníkem, pokud to není

nezbytné pro oddělení více příkazů na jednom řádku.

2.1.1 Třídy

Podle webových stránek Classes and inheritance, definice třídy obsahuje klíčové

slovo class a umožňuje vytvořit konstruktor přímo v definici. Pro zapsání

dědičnosti se namísto extends nebo implements používá dvojtečka pro třídy

i rozhraní.

Page 12: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

4

class Person

class City(name: String)

open class Parent

class Child : Parent()

V předchozím příkladu kódu je vidět, že v Kotlinu jsou složené závorky nepovinné,

pokud není potřeba uvedení více příkazů v bloku. Třída Person je příkladem

nejjednoduššího možného zápisu třídy. Třída City obsahuje deklaraci konstruktoru

s jedním parametrem, který je možné použít pro inicializaci atributů třídy. Třída

Parent slouží jako předek pro třídu Child, definice musí obsahovat klíčové slovo

open, protože třídy v Kotlinu jsou implicitně final. Třída Child je potomkem třídy

Parent, jejíž konstruktor musí v deklaraci dědičnosti volat. [2]

2.1.2 Konstruktory

Konstruktory se v Kotlinu umísťují do definice třídy, ale mohou být stejně jako

v Javě definovány uvnitř třídy pomocí klíčového slova constructor, v definici

konstruktoru je možné deklarovat atributy třídy pomocí klíčových slov var/val,

které jsou pak inicializovány hodnotami předanými do konstruktoru.

class Point(val x: Int = 0, val y: Int = 0)

class AppControlledInstantiation internal constructor()

class Company {

constructor() {

}

}

class Employee(val name: String, var age: Int)

Konstruktor třídy Point ukazuje možnost definice defaultních hodnot pro

parametry konstruktoru, takže při volání konstruktoru je pak definice těchto

parametrů volitelná. Třída AppControlledInstantiation představuje příklad

definice konstruktoru, který je přístupný pouze ze tříd daného modulu.

Třída Company ukazuje definici konstruktoru uvnitř třídy.

Anotace konstruktoru je možné použít v případě zápisu jako u tříd

AppControlledInstantiation a Company.

Page 13: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

5

Kód třídy Employee odpovídá následujícímu kódu v Javě:

public class Employee {

private final String name;

private int age;

public Employee(String name, int age) {

this.name = name;

this.age = age;

}

public String getName() {

return name;

}

public int getAge() {

return age;

}

public void setAge(int age) {

this.age = age;

}

}

Na předchozím kódu je vidět, o kolik méně kódu, oproti Javě, je nutné napsat

v Kotlinu oproti Javě při zachování stejné funkcionality, což přispívá k lepší

čitelnosti kódu. Více informací a příkladů je uvedeno na webových stránkách Kotlin

Programming Language. [2]

2.1.3 Funkce

Jak je uvedeno na webových stránkách Functions, funkce je obdoba metody v Javě,

její definice obsahuje klíčové slovo fun, stejně jako třídy jsou i funkce implicitně

final, takže pro jejich překrytí je nutné použít klíčové slovo open. Syntaxe funkce,

stejně jako mnoho ostatních věcí v Kotlinu, umožňuje vynechat nepotřebné prvky

syntaxe. Například návratový typ v případě, kdy má funkce návratový typ Unit

(obdoba void) nebo je použita syntaxe návratové hodnoty s = (jednořádkové

funkce). Dalším rozdílem oproti Javě je, že funkce nemusí být definovány uvnitř

třídy, ale mohou být definovány v rámci balíčku. Stejně jako u konstruktorů je i pro

parametry metod možné použít defaultní hodnoty. Kotlinu obsahuje podporu pro

takzvané extension funkce, díky kterým lze přidávat funkcionalitu do existujících

tříd, i když není možné přímo upravovat zdrojový kód těchto tříd. [3]

Page 14: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

6

fun sum(a: Int, b: Int): Int {

return a + b;

}

fun times(a: Int, b: Int) = a * b

fun String.isLongerThan10() = this.length > 10

fun testIsLongerThan10() {

"hello".isLongerThan10() == false

}

Funkce sum představuje standardní zápis funkce včetně návratové hodnoty,

zatímco funkce times ukazuje, jak je možné využít kratší zápis pro jednořádkové

funkce.

String.isLongerThan10 představuje zápis extension funkce. Pokud je funkce

definována tímto způsobem, je pak možné ji volat nad objekty dané třídy. V tomto

případě bude možné volat funkci isLongerThan10 nad jakýmkoliv Stringem.

Zároveň ukazuje, že tělo funkce se definuje, jako by daná třída byla opravdu v dané

třídě, tedy klíčové slovo this odkazuje na současnou instanci třídy String, nad

kterou je funkce zavolána. V tomto případě by bylo možné identifikátor this

vynechat, protože název atributu length nekoliduje s žádnou lokální proměnnou.

Funkce testIsLongerThan10 ukazuje použití extension funkce a také možnost

vynechat návratový typ, pokud funkce nic nevrací.

2.1.4 Proměnné

Proměnné jsou základem každého programovacího jazyka, Kotlin obsahuje

stručnou a jednoduchou syntaxi pro jejich používání. Deklarace proměnné obsahuje

jedno z klíčových slov var/val pro odlišení, zda bude proměnná konstantní val

nebo variabilní var. Typ proměnné je možné vynechat v případě okamžitého

přiřazení hodnoty. Další informace na webových stránkách Properties and Fields.

[4]

Page 15: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

7

var a: Int = 0

var b = 0

var c: Int

c = 0

a++

val d = 0

val e: Int

e = 0

/*

a = "string"

e++

*/

V předchozím příkladu je ukázka různých kombinací deklaraci a definicí

proměnných s uvedením typu proměnné a přiřazením hodnoty. A také

zakomentované příklady statické kontroly typu při kompilaci a nemožnost změny

hodnoty konstantní proměnné.

2.1.5 Řízení běhu programu

Další běžnou součástí většiny programovacích jazyků jsou konstrukty pro řízení

běhu programu, mezi které v Kotlinu patří if - else, when, while, for, return, break

a continue.

Podmínka if-else

if (input.isLongerThan10()) {

processLongString(input)

} else {

processShortString(input)

}

val result = if (input.isLongerThan10()) "long" else "short"

val list: List<Int> = createSomeList()

if (list is LinkedList<Int>) {

list.push(3)

}

První podmínka v příkladu ukazuje klasické užití konstruktu if – else. Druhý if

ukazuje funkci Kotlinu, která umožňuje „vrátit“ z podmínky hodnotu posledního

příkazu v daném bloku, zde tedy bude proměnná result obsahovat řetězec „long“

nebo „short“ v závislosti na délce vstupního řetězce. Poslední podmínka ukazuje

možnost automatického přetypovaní při ověřování. Obdobou v Javě je:

final List<Integer> list = createSomeList();

if (list instanceof LinkedList<Integer>) {

((LinkedList<Integer>) list).push(3);

}

Page 16: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

8

Přepínač when

When je rozšířenou obdobou přepínače switch z Javy. Jeho možnosti jsou patrné

z následujícího příkladu:

val apologiesList: List<String> = listOf("sorry", "my apologies")

when (input) {

"hi" -> sayHello()

"bye", "goodbye" -> sayGoodbye()

in apologiesList -> apologize()

}

val list: List<Int> = createSomeList()

when (list) {

is LinkedList<Int> -> list.push(3)

is Stack<Int> -> list.search(3)

}

val result = when {

input.isLongerThan10() -> "long"

input.isEmpty() -> "empty"

else -> "short"

}

První when ukazuje možnosti použití pro zavolání různých funkcí na základě vstupu,

je možné použít konkrétní hodnotu, více hodnot nebo vyhledání dané hodnoty

v kolekci. Druhý when představuje možnosti automatického přetypování na daný

typ. A ve třetím příkladu je vidět vlastnost when pro přiřazení hodnoty proměnné na

základě výsledku funkcí volaných nad objektem vstupní proměnné.

Cyklus while a do-while

While a do-while vypadají a fungují stejně jako odpovídající konstrukty v Javě,

s výjimkou toho, že v do-while lze deklarovat řídící proměnnou cyklu uvnitř bloku.

var i = 10

while (i > 0) {

i--

}

do {

val y = readLineFromFile()

} while (y != null)

Cyklus for

Základní podoba for cyklu v Kotlinu je podobná for-each cyklu v Javě. Lze ho použít

pro iteraci přes jakýkoliv objekt, který je schopen vrátit iterátor. Klasický for cyklus,

který iteruje pomocí indexu v Kotlinu není, ale je možné ho nahradit použitím

takzvané Range.

Page 17: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

9

val list = createSomeList()

for (i in list) {

doSomethingWithItem(i)

}

for ((index, value) in list.withIndex()) {

println("list contains $value at index $index")

}

for (i in 1..10 step 2) {

println("writing range 1-10 inclusive with step 2: $i")

}

První for představuje použití jako for-each cyklus s tím, že uvedení typu prvku

kolekce je volitelné. Druhý cyklus ukazuje možnost převedení prvků kolekce na

indexované položky, takže je možné v bloku for cyklu přistupovat jak k indexu, tak

i k prvku kolekce. Poslední for iteruje přes objekt Range a je obdobou klasického

for cyklu z Javy, v tomto případě objekt Range bude obsahovat čísla 1, 3, 5, 7 a 9.

V Kotlinu Range může obsahovat i jiné objekty než jen čísla, např.: data nebo je

možné vytvořit si vlastní implementaci Range a iterovat přes ni.

Operátory return, break a continue

Operátory return, break a continue fungují stejně jako v Javě se syntaktickou

výjimkou pro skok na label.

val list: List<List<Int>> = createSomeListOfLists()

loop@ for (i in list) {

for (j in i) {

if (j == 3) {

break@loop

} else if (j == -1) {

continue@loop

}

}

}

2.1.6 Modifikátory viditelnosti

Podle webových stránek Visibility Modifiers jsou v Kotlinu 4 modifikátory

viditelnosti, a to private, protected, internal a public. Platí, že pokud není

uveden žádný identifikátor, je použit public.

Page 18: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

10

Tabulka 1 modifikátory viditelnosti uvnitř třídy.

Uvnitř třídy Třída potomek modul kdekoliv

private x

protected x x

internal x x

public x x x x

Zdroj: data pro tabulku [5]

Tabulka 2 modifikátory viditelnosti v rámci balíčku.

Uvnitř balíčku Soubor modul kdekoliv

private x

internal x x

public X x x

Zdroj: data pro tabulku [5]

Na rozdíl od Javy vnější třída nemůže přistupovat k private prvkům vnitřní třídy.

Dalším rozdílem je, že k internal prvkům nelze přistupovat z potomků v jiných

modulech.

Modul, který určuje viditelnost internal je modul v rámci IDE IntelliJ IDEA, Maven,

Gradle projekt nebo množina souborů zkompilovaná v rámci jednoho volání Ant

tasku. [5]

2.2 Pokročilé vlastnosti

V kapitole 2.1 byly představeny základní prvky Kotlinu, které jsou podobné funkcím

Javy, ve většině případů rozšířené o některé užitečné funkce. V této kapitole budou

ukázány prvky, které patří mezi pokročilé funkce Javy nebo v Javě vůbec nejsou.

Také zde budou popsány podrobněji funkce, které byly stručně uvedeny v kapitole

2.1, ale mají další rozšiřující funkcionalitu, která nebyla vysvětlena v rámci základní

funkcionality.

2.2.1 Null reference

Jak je uvedeno na webových stránkách Kotlin Programming Language, Kotlin se

snaží vyhýbat používání null reference tím, že standardně všechny proměnné

a parametry jsou ne-nullové, to znamená, že do nich nelze uložit null. Kvůli

kompatibilitě s Javou bylo ovšem nutné nějak zajistit, aby do metod a proměnných

Page 19: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

11

bylo možné null uložit, proto pro takzvané nullable proměnné existuje odlišná

syntaxe. Také je možné z nullable proměnné udělat normální proměnnou, např.:

automatickým přetypováním takzvaným null-checkem - klasický if ověřující, že

proměnná neobsahuje null. [6]

var nullableVariable: String? = null

var classicVariable: String = ""

/*

classicVariable = null

classicVariable = nullableVariable

*/

if (nullableVariable != null) {

classicVariable = nullableVariable

}

classicVariable = nullableVariable ?: ""

classicVariable = nullableVariable!!

val nullableObject: NullableObject? = getNullableObject()

var nullableResult: String =

nullableObject

?.returnNullableService()

?.returnNullableResult() ?: "unable to get result"

var codeThrowingNPE: String? =

nullableObject!!

.returnNullableService()!!

.returnNullableResult()

V tomto příkladu je vidět, že nullable typy se označují pomocí otazníku a že není

možné přiřadit null, ani nullable proměnnou přímo do klasické proměnné, takový

kód nebude možné zkompilovat. První možnost jak to udělat je pomocí null-checku,

který automaticky přetypuje String? na typ String. Druhou možností je použít

takzvaný elvis operátor „?:“, který v případě, že proměnná obsahuje null, přiřadí

do classicVariable pravý operand tohoto operátoru. Třetí možností je použít

non-null aserci v podobě operátoru „!!“, který zajistí vyhození

NullPointerException v případě, že proměnná obsahuje null.

Další příklad ukazuje null-safe volání, které zajišťuje, že pokud některá metoda

v řetězci volání vrátí null, kód nezpůsobí vyhození NullPointerException, ale

místo toho vrátí null a při použití elvis operátoru se do proměnné uloží pravý

operand tohoto operátoru.

A poslední příklad ukazuje kód, který se bude v případě nullové návratové hodnoty

chovat stejně jako Java – vyhodí NullPointerException.

Page 20: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

12

2.2.2 Data třídy

Při programování v Javě jsou velmi často vytvářeny třídy, které mají jedinou funkci

– přenášet data. Většina kódu těchto tříd je často generována pomocí IDE, jedná se

především o gettery, settery, konstruktory a metody equals, hashCode a toString.

V Kotlinu lze tyto třídy označit klíčovým slovem data. Při kompilaci jsou všechny

tyto funkce a konstruktory vygenerovány, navíc jsou generovány další funkce,

například componentN, kde N odpovídá pořadí definovaných vlastností. Tyto metody

umožňují rozložení objektu na množinu proměnných.

Další z generovaných funkcí je copy, která může být využita pro zkopírování objektu

a změnu některých jeho vlastností. Pokud některá z těchto generovaných metod je

už ve třídě definována nebo zděděna, nebude generována. Více na webových

stránkách Kotlin Programming Language. [7]

2.2.3 Generické typy

Jak je psáno na webových stránkách Kotlin Programming Language, generické typy

jsou používány pro psaní tříd a funkcí, které mohou pracovat s předem

nespecifikovaným typem a přitom nemusel být použit typ Object nebo Any

v případě Kotlinu, a tím obětovat statickou typovou bezpečnost. Typickým

příkladem a nejčastěji používaným generickým typem jsou kolekce. Ty umožňují

stejné operace nad jakýmkoliv typem a pomocí generických typů je možné zajistit,

aby kolekce mohly obsahovat pouze prvky daného typu, což by v případě použití

Object nebo Any nebylo možné. Při používání generických typů je někdy nutné

specifikovat dodatečné podmínky pro dané typy, například, že je možné použít jen

typy implementující určitý interface. V Javě je toto zajištěno pomocí klíčového slova

extends. V Javě je možné napsat <T extends Closeable> což znamená, že typem

musí být třída implementující interface Closeable, potom je možné na proměnné

typu T volat metodu close. V Kotlinu je klíčové slovo extends nahrazeno za

dvojtečku a více typů je možné specifikovat pomocí klauzule where. [8]

Page 21: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

13

fun <T> multiplyByTwoAndClose(items: T)

where T: Iterable<Int>,

T: Closeable {

for (item in items) {

doSomethingWithItem(item * 2)

}

items.close()

}

Jak píše Bloch, jednou z neočekávaných vlastností tříd a metod používajících

generické typy je, že nejsou vzájemně kompatibilní, i když jejich generické typy

kompatibilní jsou [1: s. 134]. Například následující zakomentovaný Java kód by

nešel zkompilovat, i když je Integer potomkem Number.

public static void main(String[] args) {

Stack<Number> stack = new Stack<>();

List<Integer> integers = Arrays.asList(1, 2);

// stack.pushAll(integers);

}

public static class Stack<E> {

public void push(E e) {}

public E pop() {return null;}

public boolean isEmpty() {return false;}

public void pushAll(Iterable<E> src) {

for (E e : src)

push(e);

}

}

Řešením je použití takzvaného „bounded wildcardu“. Ten umožní, že je možné do

metody předat parametr jakéhokoliv typu, který je typově kompatibilní s daným

typem E. Takto by vypadala výsledná metoda pushAll.

public void pushAll(Iterable<? extends E> src) {

for (E e : src)

push(e);

}

Při použití tohoto parametru, bude možné zakomentovaný kód v předchozím

příkladu zkompilovat. Opačným případem je metoda popAll v následujícím

příkladu.

Stack<Integer> stack = new Stack<>();

List<Number> integers = new ArrayList<>();

// stack.popAll(integers);

public void popAll(Collection<E> dst) {

while (!isEmpty())

dst.add(pop());

}

Zde je pro kompilaci zakomentovaného kódu nutné změnit parametr dst na typ

Collection<? super E>. Bloch tyto typy pojmenovává jako consumers respektive

Page 22: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

14

producers a navrhuje mnemotechnickou pomůcku, jak si zapamatovat, kdy jaké

klíčové slovo použít „PECS stands for producer-extends, consumer-super“ [1: s. 136].

Tuto mnemotechnickou pomůcku není nutné při používání Kotlinu znát, protože

typy označované jako producers používají klíčové slovo out a typy consumers

používají klíčové slovo in. Pro porovnání by funkce pushAll a popAll v Kotlinu

vypadaly následovně:

fun pushAll(src: Iterable<E>) {

for (e in src)

push(e)

}

fun popAll(dst: MutableCollection<in E>) {

while (!isEmpty)

dst.add(pop())

}

Protože rozhraní Iterable v Kotlinu už má generický parametr označen jako out,

není nutné jej specifikovat v použité funkci.

Kotlin navíc podporuje použití generických typů v extension funkcích, takže je

možné „přidat“ funkce do předem nespecifikovaných tříd.

fun <T, E> T.assertArraySize(array: Array<E>, expectedSize: Number) {

if (array.size != expectedSize) {

Assert.fail()

}

}

Takto lze přidat další typy assert funkce do testů, aniž by musely dědit od společné

třídy, ve které by tyto funkce byly definovány, nebo používat pomocnou třídu se

statickými metodami.

2.2.4 Lambda výraz

Lambda výraz v Kotlinu je obdobou closure z Groovy a lambda výrazu z Javy 8. Lze

ho využít pro předávání funkcionality do funkcí jako parametr, ukládat do

proměnných nebo použít jako návratovou hodnotu funkce.

Page 23: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

15

fun lambdaSyntax() {

val plus = { x: Int, y: Int -> x + y }

val minus: (Int, Int) -> Int = { x, y -> x - y }

val isOdd = { x: Int -> x % 2 == 1 }

val isEven: (Int) -> Boolean = { it % 2 == 0 }

val ints = createSomeList()

val oddIntsSquared = ints.filter(isOdd).map { it * it }

}

Předchozí příklad ukazuje různé zápisy lambda výrazů i jejich použití pro práci

s kolekcemi.

2.2.5 Delegates

Jak uvádí webová stránka Kotlin Programming Language, pro definici vlastnosti

třídy v Kotlinu je možné delegovat její inicializaci na tzv. delegates. Delegate je

objekt, který obsahuje funkce getValue a pro nekonstantní vlastnosti i setValue,

které jsou volány při přístupu k delegované vlastnosti. Mezi delegates, které jsou

obsaženy přímo v knihovně Kotlinu, patří: lazy, observable, vetoable, notNull

a map. Lazy delegate lze použít pro odloženou inicializaci hodnoty proměnné, takže

její hodnota je vypočítána při prvním přístupu k vlastnosti a je inicializována pomocí

lambda výrazu předaného do tohoto delegate jako parametr. Pravidla pro

synchronizaci při použití tohoto delegate lze nastavit pomocí volitelného

parametru. Observable delegate umožňuje definovat akci při čtení a modifikaci

vlastnosti, definovanou pomocí lambda výrazu předaného jako parametr. Tento

delegate je implementací návrhového vzoru observer pro přístup k vlastnosti. Jak

píše Pecinovský, „Zavádí vztah mezi objekty (pozorovateli) reagujícími na změnu

(pozorovaného) objektu nebo jím sledované události. Pozorovatelé se u pozorovaného

objektu přihlásí a ten je pak na každou změnu svého stavu či výskyt události upozorní.“

[8: s 375] Tento delegate neumožňuje zabránění modifikace vlastnosti. Vetoable

delegate je podobný observable s tím rozdílem, že umožňuje zabránit modifikaci

vlastnosti. NotNull delegate je vhodný pro vlastnosti, jejichž typ nemá být nullable,

ale při konstrukci objektu ještě není známa jejich hodnota. Pokud je zavolána

metoda get nad NotNull delegate, je vyhozena výjimka informující o přístupu

k neinicializované vlastnosti. Map delegate umožňuje delegovat hodnotu proměnné

na určenou instanci třídy Map, respektive MutableMap pro variabilní proměnné.

Page 24: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

16

V této mapě musí být hodnota uložena pod klíčem, který odpovídá názvu vlastnosti

[9].

class DelegatesExample(map: MutableMap<String, String>) {

val lazyProperty: ExpensiveComputation by lazy {

ExpensiveComputation()

}

var observedProperty: String by Delegates.observable("initial",

{property, old, new -> saveToDB(property, new)}

)

var vetoableProperty: Int by Delegates.vetoable(3, {

property, old, new ->

if (new >= 0) {

saveToDB(property, new)

return@vetoable true

}

return@vetoable false

})

var notNullProperty: String by Delegates.notNull<String>()

var mapProperty: String by map

}

2.3 Standardní knihovna

Standardní knihovna Kotlinu obsahuje třídy, funkce a extension funkce pro třídy ze

standardní knihovny Javy, pro usnadnění a zefektivnění práce s Kotlinem. V této

kapitole budou popsány kolekce a jejich použití v Kotlinu. A také zde budou uvedeny

některé z velkého množství obecných pomocných funkcí a extension funkcí ve

standardní knihovně Kotlinu, které zjednodušují použití často používaných vzorů.

V této kapitole bude ukázáno použití několika z nich.

2.3.1 Kolekce

Podle webových stránek Kotlin Programming language, jsou v Kotlinu použity

standardní Java kolekce nacházející se v balíčku java.util, ale používá na ně 2

základní náhledy, měnné (mutable) a neměnné (immutable). Zároveň k těmto

kolekcím přidává řadu funkcí pro lepší práci s daty v kolekcích, mnoho těchto funkcí

používá funkcionální přístup k programování a mají obdobu v Groovy nebo ve

Streaming API přidaném v Javě 8. Více informací o Streaming API je na webové

stránce Processing Data with Java SE 8 Streams [10].

Page 25: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

17

Kotlin také přidává metody pro intuitivnější vytváření a práci s kolekcemi.

Například přístup k prvkům listu nebo i mapy je stejný jako přístup k prvkům pole

v Javě [11].

val immutableMap = mapOf(

"Key" to "Value",

"SecondKey" to "SecondValue"

)

immutableMap["Key"] == "Value"

// immutableMap["SecondKey"] = "DifferentValue"

val mutableMap = mutableMapOf("Key" to "Value")

mutableMap["Key"] = "DifferentValue"

mutableMap += "ThirdKey" to "ThirdValue"

val list = listOf(1)

val arrayList = arrayListOf(0)

val listCreatedByConstructor = ArrayList<Int>()

val mutableList = mutableListOf(0)

list[0] == 1

val newList = list + arrayList

newList !== list && newList !== arrayList

newList == listOf(1, 0)

mutableList[0] = 1

mutableList += 2

mutableList == mutableListOf(1, 2)

val set = setOf(0)

val hashSet = hashSetOf(0)

val mutable: MutableList<Int> = mutableListOf(1, 2)

var immutable: List<Int> = mutable

mutable += 3

mutable == mutableListOf(1, 2, 3)

mutable.add(4)

// immutable.add(5)

// val otherMutable: MutableList<Int> = immutable

V předchozím příkladu je vidět, jak lze vytvářet kolekce a nějaké základní operace

s prvky kolekcí, včetně několika příkladů přepisování operátorů. Příklad zároveň

ukazuje, že immutable kolekce je jen pohled na kolekci skrze interface, který

neobsahuje funkce pro modifikaci kolekcí. Ale přesto je možné do nich stále přidávat

prvky, pokud k nim lze přistupovat i přes mutable pohled. Zároveň je zde ukázáno,

že na mutable kolekci lze pohlížet přes immutable interface, ale naopak to nelze,

protože by pak bylo jednoduché pouze uložit immutable kolekci do mutable

proměnné a upravovat ji, a tím by se popřel celý smysl existence immutable kolekce.

Page 26: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

18

val list = (1..5).toList()

val listWithEvenNumbers = list.filter { it % 2 == 0 }

listWithEvenNumbers == listOf(2, 4)

val grouped = list.groupBy { if (it % 2 == 0) "even" else "odd" }

grouped == mapOf(

"even" to listOf(2, 4),

"odd" to listOf(1, 3, 5)

)

val doubledList = list.map { it * 2 }

doubledList == listOf(2, 4, 6, 8, 10)

V tomto příkladu je vidět použití několika metod, které umožňu jí snadnou

transformaci kolekcí dle různých charakteristik jejich prvků.

2.3.2 Obecné funkce

V standardní knihovně je velké množství obecných pomocných funkcí a extension

funkcí, které zjednodušují použití často používaných vzorů. V této kapitole bude

ukázáno použití několika z nich.

Funkce apply, let, run, with a use

Funkce apply, let a run jsou extension funkce objektu Any, tedy všech objektů,

funkce apply po vykonání daného bloku vrací instanci, na které byla zavolána

(reciever) a daný blok je definován jako extension funkce recieveru, takže příkazy

v bloku jsou vykonány v rámci objektu reciever. Není tedy nutné specifikovat název

proměnné pro volání funkce objektu, stejně jako uvnitř všech extension funkcí.

Funkce let předává reciever jako parametr do daného bloku a vrací výsledek bloku.

Lze ji použít zejména pro omezení platnosti proměnné.

Funkce run je velmi podobná funkci let s tím rozdílem, že blok je v tomto případě

extension funkcí recieveru, takže do něj není předáván jako parametr, ale kód bloku

je vykonán v jeho rámci.

Funkce with je top-level funkce, která mění rámec uvnitř bloku na objekt, který

přijme jako parametr, je tedy obdobou funkce run, jen je možné objekt specifikovat

parametrem, místo voláním run nad daným objektem.

Funkce use je extension funkce objektů implementujících rozhraní Closeable a má

podobnou funkci jako try with resources z Javy 7. Objekt je předán jako parametr

a po dokončení bloku je nad ním zavolána metoda close.

Page 27: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

19

fun useDelegateExtensions(nullableConnection: DbConnectionProvider) {

val panel = JPanel().apply {

add(JScrollPane().apply {

add(JLabel("Fill content:"))

add(JTextArea().apply {

lineWrap = true

wrapStyleWord = true

columns = 20

rows = 5

})

})

}

nullableConnection.createConnection()?.let { conn ->

conn.beginTransaction()

conn.changeVersion()

conn.createTable("TEST")

conn.commitTransaction()

}

nullableConnection.createConnection()?.run {

beginTransaction()

changeVersion()

createTable("TEST2")

commitTransaction()

}

with(panel) {

layout = BorderLayout()

border = BorderFactory.createEtchedBorder()

background = Color.GREEN

isOpaque = true

}

File(".").bufferedReader().use {

var counter = 0

it.forEachLine {

counter++

}

println(counter)

}

}

V předchozím příkladu jsou ukázky použití metod apply, let, run, with a use.

Funkci apply je možné použit pro celkem přehledné skládání objektů a nastavení

jejich vlastností.

Funkce let je zde použita pro omezení rámce vytvořené connection, ve kterém lze

k této proměnné přistupovat. Dalším přínosem je ošetření případu, kdy funkce

createConnection vrátí null, místo vyhození NullPointerException daný blok

nebude proveden.

Funkce run je použita stejně jako funkce let, jen s rozdílem, že není nutné funkce

volat na proměnné conn.

Funkce with je užitečná zejména v případě, kdy je potřeba nastavit více hodnot na

jednom objektu, takže není nutné pokaždé specifikovat proměnnou.

Page 28: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

20

A funkce use ukazuje jak spočítat řádky v souboru s automatickým zavřením

readeru.

Funkce pro práci s konzolí

Kotlin obsahuje několik top-level funkcí, které zkracují zápis práce s konzolí. Tyto

metody jsou: print, println a readLine, které delegují práci na System.out resp.

System.in – obalený do BufferedReaderu, aby bylo možné číst řádek místo znaku.

Funkce print a println jsou přetíženy stejně jako metody v System.out.

Funkce lazy a lazyOf

Funkce lazy a lazyOf převádějí inicializační blok na instanci třídy Lazy, která

umožňuje inicializovat objekt až při prvním použití. V Kotlinu lze použít několik

typů lazy, defaultní používá synchronizaci pro zajištění, správně inicializace i ve

vícevláknovém prostředí. Další možností je publication, používá atomickou

referenci pro uložení hodnoty, ale přístup není synchronizován, při vícevláknové

inicializaci, bude použita první vrácená hodnota. Poslední možností je none, která

neobsahuje ani synchronizaci, ani atomický přístup, není tedy bezpečné ji používat

ve vícevláknovém prostředí.

Funkce lazyOf vytvoří z dané instance už inicializovanou instanci Lazy.

fun lazyFunctions(): Unit {

val noneLazy = lazy(LazyThreadSafetyMode.NONE) {

DbConnectionProvider().createConnection()

}

val publicationLazy = lazy(LazyThreadSafetyMode.PUBLICATION) {

DbConnectionProvider().createConnection()

}

val syncLazy = lazy(LazyThreadSafetyMode.SYNCHRONIZED) {

DbConnectionProvider().createConnection()

}

val initializedLazy = lazyOf(DbConnectionProvider()

.createConnection())

}

Funkce synchronized a zámky

Funkce synchronized nahrazuje synchronized konstrukt z Javy a má stejnou

syntaxi a funkci jako její obdoba z Javy, včetně zobrazení zámků v thread-dumpu. Do

tříd zámků v Javě bylo přidáno několik extension funkcí, které usnadňují jejich

správu. Tyto funkce jsou withLock pro ReentrantLock a read, write pro

Page 29: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

21

ReeantrantReadWriteLock, tyto funkce získají daný zámek, potom provedou daný

blok a zámek zase odemknou.

fun synchronization() {

synchronized(Any()) {

println("Print guarded by synchronized")

}

}

fun locks() {

val lock = ReentrantLock()

lock.withLock {

println("Print guarded by reentrant lock")

}

val rwlock = ReentrantReadWriteLock()

rwlock.read {

val value = someSharedProperty

save(value)

}

rwlock.write {

someSharedProperty = "new value"

}

}

Funkce pro práci s třídou File

Kotlin přidává mnoho extension funkcí pro práci s třídou File. Tyto funkce

usnadňují práci s obsahem souboru i s procházením adresářové struktury.

V následujícím příkladu je vidět několik funkcí, které jsou přidány.

fun fileFunctions() {

val file = File("")

file.bufferedReader()

file.bufferedWriter()

file.inputStream()

file.outputStream()

file.useLines { println("Number of Lines: ${it.count()}") }

file.forEachLine { println(it) }

val linesList = file.readLines()

file.deleteRecursively()

file.copyRecursively(File("target"))

file.walkTopDown().forEach {

println(it.absolutePath)

}

}

Funkce bufferedReader, bufferedWriter, inputStream a outputStream vytvářejí

instance odpovídajících objektů, které jsou napojeny na daný File. Funkce useLines

umožňuje definovat blok, do kterého je jako parametr předána Sequence obsahující

řádky souboru. Funkce forEachLine umožňuje definovat akci s každým řádkem

souboru. Funkce readLines vrací List obsahující všechny řádky souboru.

Page 30: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

22

Z funkcí, které pracují se soubory, nikoliv s jejich obsahem jsou v příkladu uvedeny

tyto:

deleteRecursively - rekurzivně maže adresářovou strukturu od daného

adresáře.

copyRecursively - rekurzivně kopíruje daný adresář do daného cílového

adresáře.

walkTopDown - vrací objekt Sequence, který prochází adresáře a soubory

pomocí algoritmu prohledávání do hloubky.

Funkce measureTimeMillis a measureNanoTime

Funkce measureTimeMillis a measureNanoTime umožňují vytvářet velmi

jednoduché benchmarky, pomocí těchto funkcí je možné změřit, kolik času trvalo

vykonání předaného bloku s přesností na milisekundy respektive nanosekundy.

fun benchmarking() {

val millis = measureTimeMillis {

doSomeComputation()

}

val nanos = measureNanoTime {

doSomeComputation()

}

}

2.4 Dekompilace do Javy

Obsahem této kapitoly je popis a ukázky toho, jak vypadá Kotlin po dekompilaci do

Javy, bude tedy vidět, jak je dosaženo dané funkcionality v Kotlinu.

2.4.1 Top level funkce

Top level funkce jsou funkce, které nejsou součástí žádné třídy a jsou definovány

přímo v souboru .kt. Nahrazují tak nutnost vytváření Utils tříd s množinou

statických metod.

Page 31: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

23

Následující kód ukazuje, jak vypadá zápis top level funkce a top level extension

funkce a jejich použití v Kotlinu.

fun someTopLevelFunction(first: Int, second: Int): Boolean {

return false

}

fun String.extensionFunction(first: Int): Boolean {

return length > first

}

fun useFunctions() {

val topLevel = someTopLevelFunction(1, 2)

val extension = "someString".extensionFunction(3)

}

A po dekompilaci do Javy:

public final class FunctionsKt

{

public static final boolean someTopLevelFunction(

int first, int second)

{

return false;

}

public static final boolean extensionFunction(

String $receiver, int first)

{

Intrinsics.checkParameterIsNotNull($receiver, "$receiver");

return $receiver.length() > first;

}

public static final void useFunctions()

{

boolean topLevel = someTopLevelFunction(1, 2);

boolean extension = extensionFunction("someString", 3);

}

}

V uvedeném kódu je vidět, že top level funkce jsou kompilovány do vygenerované

třídy, která přebírá název od .kt souboru a dané funkce jsou zkompilovány na

statické metody. Také je vidět, jak fungují extension funkce. Objekt, který rozšiřují

je předán jako první parametr dané funkce s názvem $reciever a na tom jsou

volány metody, případně přístup k veřejným atributům třídy.

2.4.2 Lambda výrazy

Lambda výrazy byly přidány do Javy až ve verzi 1.8, přesto je možné je v Kotlinu

používat i s použitím Javy 1.6.

Page 32: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

24

V následujícím příkladu bude vidět jak je to možné.

fun lambdas(): Unit {

val concatTwice = { param: String -> param + param }

val stringstring = useLambda(concatTwice, "string")

}

fun useLambda(lambda: (String) -> String, param: String)

= lambda(param)

po dekompilaci: (výpis byl upraven, kvůli přehlednosti a stručnosti)

public final class LambdaKt {

public static final void lambdas() {

Function1 concatTwice =

(Function1) lambdas.concatTwice._cls1.INSTANCE;

String stringstring = useLambda(concatTwice, "string");

}

public static final String useLambda(Function1 lambda,

String param) {

Intrinsics.checkParameterIsNotNull(lambda, "lambda");

Intrinsics.checkParameterIsNotNull(param, "param");

return (String) lambda.invoke(param);

}

public static final class lambdas.concatTwice._cls1

extends Lambda implements Function1 {

public volatile Object invoke(Object obj) {

return invoke((String) obj);

}

public final String invoke(String param) {

Intrinsics.checkParameterIsNotNull(param, "param");

return (new StringBuilder())

.append(param).append(param).toString();

}

public static final lambdas.concatTwice._cls1 INSTANCE

= new lambdas.concatTwice._cls1();

}

}

Z předchozího výpisu je vidět, že lambda výraz je zkompilován jako třída, která dědí

od třídy Lambda a implementuje interface Function1, které reprezentuje funkci

s jedním parametrem a jednou metodou invoke, která v tomto případě nemá žádné

generické parametry, takže typ parametru i návratové hodnoty je Object. Třída

obsahuje i druhou metodu invoke, která obsahuje tělo lambda výrazu a je volána při

volání lambda výrazu prostřednictvím metody invoke z interface Function1, která

převádí parametry na odpovídající typy a volá danou implementaci lambda výrazu.

Page 33: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

25

2.4.3 Přiřazení konstruktu pro řízení běhu programu

V následujícím příkladu bude vidět, jak funguje přiřazení hodnoty proměnné na

základě vyhodnocení if else podmínky nebo when přepínače.

fun statementAssignment(input: String, intInput: Int) {

val value = if (input.length > 5) "long" else "short"

val complex = if (input.length > 5) {

shortenAndLog(input)

} else {

log(input)

input

}

val whenStatement = when(intInput) {

2 -> "two"

3 -> "three"

5 -> "five"

else -> "unknown"

}

save(value)

save(complex)

save(whenStatement)

}

Takto vypadá kód po dekompilaci:

public static final void statementAssignment(

String input, int intInput) {

Intrinsics.checkParameterIsNotNull(input, "input");

String value = input.length() <= 5 ? "short" : "long";

String complex;

if(input.length() > 5) {

complex = shortenAndLog(input);

} else {

log(input);

complex = input;

}

String whenStatement;

String s;

switch(intInput) {

case 2: // '\002'

s = "two";

break;

case 3: // '\003'

s = "three";

break;

case 5: // '\005'

s = "five";

break;

case 4: // '\004'

default:

s = "unknown";

break;

}

whenStatement = s;

save(value);

save(complex);

save(whenStatement);

}

Page 34: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

26

Jak je vidět, jedná se o jednoduché převedení s malou výjimkou v podobě nahrazení

jednoduchého if-u ternárním operátorem. V ostatních případech se definuje

proměnná před konstruktem a uvnitř podmínky se naplní danou hodnotou.

V případě when se ještě použije další pomocná proměnná.

2.4.4 Defaultní hodnoty parametrů

V Kotlinu je možné pro parametry konstruktorů a funkcí definovat defaultní

hodnoty, takže pak tyto parametry není nutné specifikovat při volání dané funkce

nebo konstruktoru. V následujícím příkladu je ukázáno, jak je toho dosaženo.

fun useDefaultParams() {

tryConnection("jdbc:hsqldb:file:embeddeddb")

tryConnection("jdbc:hsqldb:file:embeddeddb", 300)

tryConnection("jdbc:hsqldb:file:embeddeddb",

useTransactions = false)

tryConnection("jdbc:hsqldb:file:embeddeddb", 300, false)

}

fun tryConnection(url: String,

timeout: Int = 120,

useTransactions: Boolean = true) {

connectToDB(url, timeout, useTransactions)

}

Při použití defaultních hodnot je vytvořena nová metoda, která přidává další

parametry. Jeden určuje, které hodnoty mají být nastaveny na defaultní hodnotu

a druhý slouží pro ověření, zda metoda není volána z potomka bez specifikace všech

hodnot pomocí identifikátoru super.

Page 35: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

27

Po dekompilaci:

public static final void useDefaultParams()

{

defaultParameters$default(

"jdbc:hsql://embeddeddb", 0, false, 6, null);

defaultParameters$default(

"jdbc:hsql://embeddeddb", 300, false, 4, null);

defaultParameters$default(

"jdbc:hsql://embeddeddb", 0, false, 2, null);

defaultParameters("jdbc:hsql://embeddeddb", 300, false);

}

public static final void defaultParameters(

String url, int timeout, boolean useTransactions)

{

Intrinsics.checkParameterIsNotNull(url, "url");

connectToDB(url, timeout, useTransactions);

}

public static volatile void defaultParameters$default(

String s, int i, boolean flag, int j, Object obj)

{

if(obj != null)

throw new UnsupportedOperationException(

"Super calls with default arguments not supported

in this target, function: defaultParameters");

if((j & 2) != 0)

i = 120;

if((j & 4) != 0)

flag = true;

defaultParameters(s, i, flag);

}

Page 36: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

28

3 Aplikace pro podporu dne otevřených dveří

Aplikace pro podporu dne otevřených dveří má 2 základní části: mobilní aplikaci

a serverovou část pro uložení informací. Obě části jsou psané v programovacím

jazyku Kotlin popsaném ve 2. kapitole.

Mobilní aplikace obsahuje funkce pro podporu účastníka dne otevřených dveří. Je

možné si zobrazit informace o škole, fakultě a o dni otevřených dveří. Také

poskytuje možnost přejít na mapu s polohou fakulty informatiky a managementu

pomocí Google Maps, takže lze zapnout navigaci k fakultě. Dále aplikace nabízí

sestavení plánu dne otevřených dveří s upozorňováním na naplánované akce.

3.1 Model

Obě části aplikace mají dva základní prvky, které jsou stejné. Část umožňující práci

s informacemi o dni otevřených dveří, fakultě a univerzitě a část určenou pro práci

s událostmi dne otevřených dveří.

3.1.1 Serverová část

Serverová část slouží pro ukládání a obsluhu informací, které jsou zobrazeny

v mobilní aplikaci. Jedná se o webovou aplikaci, obsahující RESTful API, jednoduché

webové rozhraní a databázovou část. Pomocí webového rozhraní lze pracovat

s informacemi uloženými v databázové části.

Metody měnící data jsou zabezpečeny autentizací pro zabránění neoprávněné

změně informací. Serverová část obsahuje přiloženou databázi i webový kontejner,

takže není nutné tyto části nastavovat zvlášť. Pro spuštění aplikace tedy stačí

nainstalovaná Java.

Serverová část má dvě základní části. Část obsahující informace a REST metody pro

práci s nimi a část určenou pro práci a ukládání událostí dne otevřených dveří.

3.1.2 Mobilní část

Mobilní část umožňuje uživateli čtení informací zadaných v serverové části. Včetně

cachování dat, takže informace jednou stažené jsou dostupné, i pokud se v dané

chvíli nelze připojit k serverové části. Dále se lze z aplikace přepnout do Google

Maps, kde bude zobrazena poloha Fakulty informatiky a managementu, s možností

Page 37: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

29

zapnutí navigace. Také je možné vybírat si ze seznamu nabízených akcí ty, které chce

daný uživatel navštívit a jejich export do Google Calendar.

Přihlášené události jsou ukládány do databáze s nastaveným časem. A pokud si je

uživatel odhlásí, jsou z databáze tyto záznamy odebrány, takže v databázi jsou vždy

uloženy jen přihlášené akce.

3.2 Implementace

V této kapitole bude uveden popis implementace obou části aplikace, včetně

použitých frameworků, stručného popisu jejich funkcí a popsané zdrojové kódy

aplikace.

Jak je uvedeno v úvodu 3. kapitoly, obě části jsou implementovány v jazyce Kotlin.

Pro správu závislostí serverové i mobilní části byl použit nástroj Gradle. A pro

správu závislostí ve webovém rozhraní serverové části byl použit balíčkovací

systém Node.js. Zdrojové kódy byly spravovány pomocí verzovacího systému

Mercurial.

3.2.1 Serverová část

Serverová část je centrální část, ke které se připojují všechny mobilní části

a získávají odtud data. Tato část využívá možnosti Spring frameworku, konkrétně

jeho části Spring Boot v kombinaci s ORM frameworkem Hibernate pro práci

s databází, včetně nastavené přiložené HSQLDB. Spring Framework také slouží pro

správnou inicializaci a nastavení přiloženého webového kontejneru Tomcat, včetně

správného namapování REST metod pro práci s uloženými daty. Pro vytvoření

webového rozhraní byl použit framework Bobril, umožňující vytváření rozhraní

pomocí TypeScriptu, s následným generováním HTML z virtuálního DOMu.

Spring Framework

Jak je uvedeno v Professional Java Development with the Spring Framework, Spring

Framework byl původně primárně určen pro podporu architektury MVC,

dependency injection v Javě [12], ale postupným vylepšováním se z něj stala

platforma, která podporuje širokou škálu funkcí, které výrazně ulehčují

Page 38: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

30

implementaci standardních vzorů, používaných v aplikacích různých velikostí, od

malých jednoúčelových aplikací, až po velké enterprise aplikace.

Funkce, které využívá aplikace vyvíjená v rámci této práce, jsou:

Spring Boot – automatická konfigurace používaných částí Spring

Frameworku.

Spring Data – práce s databází a daty v ní uloženými, včetně mapování

databázových tabulek na objekty, pomocí frameworku Hibernate

Spring Web – přiložený webový kontejner a konfigurace kontrolerů REST

API

Spring Security – zabezpečení REST API pomocí uživatelského jména a hesla

Spring Context – dependency injection potřebných částí aplikace

Spring Framework obsahuje mnoho dalších funkcí, které zde nebyly popsány,

protože nejsou součástí aplikace.

Hibernate

Jak je uvedeno v Hibernate in action, Hibernate je takzvaný ORM (object/relational

mapping) framework. Jehož hlavní funkcí je vyřešení problému různé interpretace

dat v relační databázi a objektově orientovaném programovacím jazyce. [13]

Umožňuje práci s databází prostřednictvím standardních Java objektů a jejich

převod do databázových řádků a zpět. Takže je možné odstínit aplikaci od práce

s připojením k databázi, SQL příkazy a transakcemi a jejich manuálním

zpracováním.

Bobril

Jak je psáno na Bobril – I – Getting Started, Bobril je komponentově orientovaný

Framework inspirovaný ReactJs a Mithril. Bobril je zaměřený na automatické

generování kódu s důrazem na rychlost a malou velikost [14].

Aplikace pro podporu dne otevřených dveří

V této části bude popsáno jak je vlastní serverová část implementována

s poznámkami k implementaci.

Page 39: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

31

Vstupním bodem Serverové části je soubor Application.kt obsahující main funkci,

která deleguje spuštění aplikace na třídu SpringApplicationBuilder

z frameworku Spring Boot, která odpovídá za automatickou konfiguraci Spring

a Hibernate frameworků. Dále je v tomto souboru definována třída Application

s anotací SpringBootApplication, podle které Spring Boot hledá konfigurační třídy.

Tato třída také implementuje rozhraní CommandLineRunner, které obsahuje metodu

run a slouží pro definici vlastní akce po nastartování a nakonfigurování aplikace.

V metodě run jsou do databáze vloženy nějaké základní testovací informace

a události. Tato třída obsahuje dvě privátní vlastnosti, do kterých jsou pomocí

dependency injection ve Spring frameworku vloženy odpovídající instance.

@SpringBootApplication

open class Application() : CommandLineRunner {

@Autowired

private lateinit var infoRepository: InfoRepository

@Autowired

private lateinit var eventRepository: EventRepository

override fun run(vararg args: String) {

infoRepository.save(Info("FIM", "Some info about FIM"))

val sdf = SimpleDateFormat("dd.MM.yyyy HH:mm")

eventRepository.save(Event(-1, "First",

sdf.parse("24.06.2016 08:00"),

sdf.parse("24.06.2016 08:30"),

"Description first", "J17"))

eventRepository.save(Event(-1, "Second",

sdf.parse("24.06.2016 08:15"),

sdf.parse("24.06.2016 08:45"),

"Description second", "J16"))

eventRepository.save(Event(-1, "Third",

sdf.parse("24.06.2016 09:00"),

sdf.parse("24.06.2016 09:30"),

"Description third", "J17"))

eventRepository.save(Event(-1, "Fourth",

sdf.parse("24.06.2016 09:15"),

sdf.parse("24.06.2016 09:45"),

"Description fourth", "J16"))

}

}

fun main(args: Array<String>) {

SpringApplicationBuilder(Application::class.java)

.registerShutdownHook(true)

.build()

.run(*args)

}

Další důležitou částí aplikace jsou třídy reprezentující informace a události.

Page 40: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

32

V serverové části jsou to třídy Info a Event. Obě třídy mají anotaci Entity, pomocí

které Spring a Hibernate poznají třídy určené pro mapování do relační databáze.

Třída Info je jednoduchá data třída, která obsahuje 2 vlastnosti: name a value, kde

name obsahuje jméno popisující konkrétní část informací a value samotné

informace. Obě vlastnosti jsou anotovány pomocí anotací, které upřesňují, jak bude

vypadat při uložení do databáze. Anotace Id určuje primární klíč.

@Entity

data class Info(

@Id

@Column(name = "INFO_NAME")

var name: String = "",

@Column(name = "INFO_VALUE", length = Short.MAX_VALUE.toInt())

@Lob

var value: String = ""

)

Data třída Event má vlastnosti id, name, start, end, description a room, pro uložení

id v databázi, názvu, začátku, konce, popisku a místnosti, kde se událost koná.

Anotace GeneratedValue umožňuje automatické generování hodnoty ve sloupci.

@Entity

data class Event(

@Id

@GeneratedValue(strategy = GenerationType.AUTO)

@Column(name = "ID")

var id: Int = -1,

@Column(name = "NAME", unique = true)

var name: String = "",

@Column(name = "START")

var start: Date = Date(),

@Column(name = "END")

var end: Date = Date(),

@Lob

@Column(name = "DESCRIPTION", length = Short.MAX_VALUE.toInt())

var description: String = "",

@Column(name = "ROOM")

var room: String = ""

)

Další důležitou částí jsou repository, které pracují s informacemi v databázi

a umožňují ukládat, měnit a vyhledávat data. Jak je psáno v dokumentaci Spring

Data frameworku, cílem Spring Data je snížit množství nadbytečného kódu při

implementaci přístupu k databázi [15]. Framework umožňuje vytvářet repository

Page 41: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

33

pomocí rozhraní, kde odpovídající implementaci vygeneruje za běhu programu,

včetně rozpoznání funkce na základě pojmenování funkcí podle určitého vzoru.

Rozhraní InfoRepository je potomkem rozhraní CrudRepository. Které jak název

napovídá, definuje některé základní metody pro práci s ukládanými objekty.

Tyto metody jsou:

save – uloží objekt do databáze

findOne – vyhledá objekt podle zadaného primárního klíče

exists – vyhledá, jestli už je objekt uložen v databázi

findAll – vrátí všechny objekty

count – vrátí počet záznamů v databázi

delete – vymaže objekt

deleteAll – vymaže všechny objekty

Rozhraní InfoRepository tedy nemusí definovat žádné další funkce, protože vše

potřebné už je zděděno z CrudRepository.

Rozhraní EventRepository také dědí od CrudRepository, ale navíc definuje funkce

findByName a findByRoom, který v databázi vyhledá událost s daným názvem,

respektive události v dané místnosti.

interface InfoRepository : CrudRepository<Info, String>

interface EventRepository : CrudRepository<Event, Int> {

fun findByName(name: String): Event

fun findByRoom(room: String): List<Event>

}

Výše byly popsány třídy týkající se ukládání dat. V další části budou ukázány třídy

zajišťující práci REST API a jeho zabezpečení. Třída InfoController definuje REST

rozhraní k informacím. Tato třída má anotaci RestController, podle které Spring

rozpozná, že v této třídě budou definovány funkce, které budou obsluhovat http

requesty jim určené. Jsou zde definovány dvě vlastnosti, infoRepository

a infoTypes. Do vlastnosti infoRepository bude pomocí dependency injection

vložena instance třídy implementující rozhraní InfoRepository, tedy instance

vygenerované třídy implementující toto rozhraní. Definované funkce mají anotace

Page 42: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

34

RequestMapping, které definují vlastnosti http requestů, které zpracovávají.

Zejména tedy adresu, http metodu a případně i typ těla requestu. Parametry těchto

metod mají anotace RequestParam, respektive RequestBody, podle kterých Spring

rozpozná, kterou část requestu má předat do parametru. Z uvedeného kódu lze také

vidět, že Spring provádí automatickou konverzi mezi objektem Info a jeho

reprezentací pomocí JSONu.

@RestController

class InfoController {

@Autowired

private lateinit var infoRepository: InfoRepository

private val infoTypes = arrayOf("OpenDays", "FIM", "UHK")

@RequestMapping(value = "/info",

method = arrayOf(RequestMethod.GET))

fun getInfo(@RequestParam(name = "name", required = true)

name: String) =

infoRepository.findOne(name)

?: Info(name, "info not found")

@RequestMapping(value = "/info",

method = arrayOf(RequestMethod.PUT),

consumes = arrayOf("application/json"))

fun putInfo(@RequestBody info: Info) {

infoRepository.save(info)

}

@RequestMapping(value = "/info/types",

method = arrayOf(RequestMethod.GET))

fun getInfoTypes() = infoTypes

}

Page 43: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

35

Třída EventController definuje funkce obsluhující zpracování requestů, které

pracují s událostmi dne otevřených dveří a má následující kód.

@RestController

class EventController {

@Autowired

private lateinit var eventRepository: EventRepository

@RequestMapping(value = "/event",

method = arrayOf(RequestMethod.GET))

fun getEvents(): List<Event> {

return eventRepository.findAll().toList()

}

@RequestMapping(value = "/event",

method = arrayOf(RequestMethod.PUT),

consumes = arrayOf("application/json"))

fun putEvent(@RequestBody event: Event) {

eventRepository.save(event)

}

@RequestMapping(value = "/event",

method = arrayOf(RequestMethod.DELETE))

fun deleteEvent(

@RequestParam(value = "id", required = true)

id: Int) {

eventRepository.delete(id)

}

}

Zabezpečení REST API je implementováno pomocí Spring Security frameworku,

jehož nastavení je ve třídě SecurityConfig. V této třídě je definováno, které http

requesty budou chráněny před neoprávněným přístupem. Toto je definováno

pomocí autentizace uživatelským jménem a heslem. Requesty, které budou

zabezpečené, jsou definovány pomocí volání metody antMatchers, která umožňuje

specifikovat http metodu a adresu, která bude požadována autentizace.

Page 44: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

36

@Configuration

@EnableWebSecurity

open class SecurityConfig : WebSecurityConfigurerAdapter() {

override fun configure(http: HttpSecurity) {

http

.authorizeRequests()

.antMatchers(HttpMethod.PUT, "/info")

.fullyAuthenticated()

.and()

.authorizeRequests()

.antMatchers(HttpMethod.PUT, "/event")

.fullyAuthenticated()

.and()

.authorizeRequests()

.antMatchers(HttpMethod.DELETE, "/event")

.fullyAuthenticated()

.and()

.authorizeRequests()

.anyRequest()

.permitAll()

.and()

.httpBasic()

.and()

.csrf().disable()

}

override fun configure(auth: AuthenticationManagerBuilder) {

auth.inMemoryAuthentication()

.withUser("**********")

.roles("***********")

.password("***********")

}

}

Obr. 1 Stránka pro úpravu informací.

Zdroj: vlastní zpracování

Page 45: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

37

Obr. 2 Stránka pro úpravu událostí.

Zdroj: vlastní zpracování

3.2.2 Mobilní část

Mobilní část je aplikace určená pro mobilní zařízení se systémem Android. Pro její

implementaci bylo použito standardní SDK Androidu, konkrétně API verze 17.

Aplikace je tedy kompatibilní se zřízeními se systémem Android 4.2 a vyšší. Jak bylo

uvedeno na začátku kapitoly 3, samotná aplikace je naprogramována v jazyce Kotlin.

Pro připojení k REST API serverové části byla využita mobilní verze knihovny Spring

android rest template, která pomocí jednoduchého rozhraní zprostředkovává volání

REST API, včetně převodu z formátu JSON na objekty.

Android SDK

Jak je popsáno v dokumentaci k Android SDK, Základem Android aplikací jsou

takzvané Aktivity, které představují jedno okno, se kterým může uživatel pracovat

a vykonat tak nějakou akci, například volat, fotit, poslat email nebo zobrazit mapu.

Okno obvykle vyplňuje celou obrazovku, ale může být i menší a být zobrazeno nad

ostatními okny. [16]

Page 46: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

38

Aktivita se skládá z třídy, která je potomkem třídy Activity a View, které

reprezentuje viditelnou část aktivity. View je obvykle implementováno pomocí xml,

které obsahuje komponenty, na které je možné se odkazovat pomocí id

a přistupovat k nim tak ze třídy, ve které je implementována logika s nimi

související.

Aktivity mají životní cyklus a různé akce mohou být prováděny v různých cyklech.

Metody životního cyklu jsou volány takto:

onCreate – při vytváření aktivity

onStart – před zobrazením

onResume – po zobrazení (i po návratu z jiné aktivity)

onPause – před přepnutím na jinou aktivitu

onStop – po skrytí aktivity

onDestroy – před ukončením aktivity

Více informací o životním cyklu lze najít v dokumentaci k Android SDK [16]

Mobilní část

Vstupním bodem mobilní části je třída MainActivity, která reprezentuje rozcestník,

pomocí kterého lze zobrazovat ostatní části aplikace. Tato aktivita obsahuje tlačítka

pro přechod na ostatní aktivity. Je zde využito několika funkcí jazyka Kotlin,

například lazy delegate, pomocí kterého je definována inicializace atributů danými

tlačítky, které jsou dostupné až po inicializaci View ve funkci onCreate a není tak

nutné jejich inicializaci provádět přímo v této metodě po inicializaci View a snižovat

tak přehlednost kódu.

Druhou funkcí je použití lambda výrazů pro definici akcí po kliknutí na dané tlačítko.

V této funkci jsou také volány funkce createDbHelpers a cacheInfoAndEvents.

Funkce createDbHelpers vytváří instance pomocných tříd pro práci s databází

a registruje je v objektu DbHelpers, aby byly instance dostupné z celé aplikace

a nemusely se vytvářet při každém použití. Funkce cacheInfoAndEvents se

v případě dostupného internetového připojení pokusí připojit k serveru, odkud

stáhne a uloží do cache informace a dostupné události. Ukázka kódu této třídy je

Page 47: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

39

zkrácena o části společné nebo podobné pro ostatní prvky, například tlačítka nebo

třídy starající se o cachování dat.

class MainActivity : Activity() {

private val btnShowMap by lazy {

findViewById(R.id.btnShowMap) as Button

}

override fun onCreate(savedInstanceState: Bundle?) {

super.onCreate(savedInstanceState)

setContentView(R.layout.activity_main)

btnShowMap.setOnClickListener {

val fimMapQuery = Uri.parse(

"geo:0,0?q=Fakulta informatiky a managementu, " +

"Hradec Králové")

val mapIntent = Intent(Intent.ACTION_VIEW, fimMapQuery)

@Suppress("UsePropertyAccessSyntax")

mapIntent.setPackage("com.google.android.apps.maps")

startActivity(mapIntent)

}

createDbHelpers()

cacheInfoAndEvents()

}

private fun createDbHelpers() {

DbHelpers.register(EventsDbHelper::class,

EventsDbHelper(applicationContext))

}

private fun cacheInfoAndEvents() {

if (isNetworkAvailable(this@MainActivity)) {

GetFromServerAndCacheTask("$SERVER_URL/event",

DbHelpers.get(EventsCacheHelper::class),

Array<Event>::class.java).execute()

} else {

Toast.makeText(this@MainActivity,

"Cannot connect to server.",

Toast.LENGTH_SHORT)

}

}

}

Objekt DbHelpers použitý v hlavní aktivitě slouží jako cache pomocných tříd pro

práci s databází. Tento objekt obsahuje jeden atribut typu ConcurrentHashMap, ve

které budou uloženy registrované třídy. K tomuto atributu bude přistupováno

z různých vláken, takže pro zajištění správného chování je použita thread-safe

implementace mapy. Jako klíč je použit KClass objekt reprezentující vkládaný

Page 48: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

40

helper. V objektu DbHelpers jsou definovány funkce get a register. Funkce get má

jeden parametr typu KClass, pomocí kterého je vyhledán odpovídající helper

v mapě. Také je díky tomu možné provést přetypování na odpovídající typ helper

třídy, takže už není nutné používat explicitní přetypování při použití funkce get.

Funkce register přebírá dva parametry, objekt třídy helperu a samotný helper.

Tato funkce používá pro uložení helperu do mapy metodu putIfAbsent, která

zajišťuje atomickou operaci vložení do mapy, pokud už tam daný objekt není.

V mapě tedy bude vždy jen první zaregistrovaný helper pro daný KClass objekt.

object DbHelpers {

private val dbHelpers =

ConcurrentHashMap<KClass<SQLiteOpenHelper>,

SQLiteOpenHelper>()

fun <T: SQLiteOpenHelper> get(clazz: KClass<in T>): T {

val helper = dbHelpers[clazz]

?: throw IllegalStateException(

"DbHelper with class: '$clazz' was not registered yet")

return helper as T

}

fun <T: SQLiteOpenHelper> register(clazz: KClass<in T>,

helper: T) {

dbHelpers.putIfAbsent(

clazz as KClass<SQLiteOpenHelper>,

helper as SQLiteOpenHelper)

}

}

Další třídou používanou v hlavní aktivitě je GetFromServerAndCacheTask. Tato třída

je potomkem třídy AsyncTaskWithListeners, která umožňuje provést operaci

v novém vlákně, aby nebylo blokováno hlavní event vlákno reagující na události od

uživatele. Tato třída má za úkol pokusit se stáhnout informace ze serveru a uložit je

do databáze. Jako parametry v konstruktoru je přebírána url serveru, cacheHelper

a třída objektu, který se bude cachovat. Pro získání dat ze serveru je použita třída

RestTemplate ze Spring Frameworku, starající se o vytvoření requestu na server

a převedení response na požadovaný objekt.

Page 49: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

41

class GetFromServerAndCacheTask<T>(val url: String,

val cacheHelper: CacheHelper<T>,

val type: Class<T>)

: AsyncTaskWithListeners<String, Unit, Unit>() {

override fun doInBackground(vararg params: String) {

val itemName = if (params.size > 0) params[0] else null

getFromServer(itemName)?.let {

cacheHelper.updateCache(it)

}

}

private fun getFromServer(name: String?): T? {

try {

RestTemplate(true).run {

(requestFactory as SimpleClientHttpRequestFactory).run {

setConnectTimeout(10 * 1000)

setReadTimeout(10 * 1000)

}

return getForObject(url, type, name)

}

} catch (e: Exception) {

Log.w("DownloadFromServer",

"Unable to download from server.", e)

return null

}

}

}

CacheHelper je rozhraní definující dvě funkce - getFromCache a updateCache, které

implementují třídy DbHelperů pro konkrétní objekty.

Abstraktní třída AsyncTaskWithListeners je potomkem třídy AsyncTask z Android

SDK, která umožňuje přidávat callbacky před začátkem zpracování akce v jiném

vlákně a po skončení vlákna, včetně práce s výsledkem zpracování. Umožňuje tak

efektivně využívat podporu lambda výrazů v Kotlinu. Kolekce, které obsahují

registrované akce, jsou instance třídy CopyOnWriteArrayList kvůli zachování

správného chování při přístupu z různých vláken. V ukázce kódu nejsou funkce

přidávající a odebírající akce z listů.

Page 50: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

42

abstract class AsyncTaskWithListeners<Params, Progress, Result>

: AsyncTask<Params, Progress, Result>() {

private val preExecuteActions: MutableList<() -> Unit> =

CopyOnWriteArrayList<() -> Unit>()

private val postExecuteActions: MutableList<(Result?) -> Unit> =

CopyOnWriteArrayList<(Result?) -> Unit>()

override fun onPreExecute() {

super.onPreExecute()

for (preExecuteAction in preExecuteActions) {

preExecuteAction()

}

}

override fun onPostExecute(result: Result?) {

super.onPostExecute(result)

for (postExecuteAction in postExecuteActions) {

postExecuteAction(result)

}

}

}

EventsDbHelper je jednou z tříd zajišťující komunikaci s databází. Tato třída dědí od

třídy SQLiteOpenHelper z Android SDK, která obsahuje metody pro usnadnění

práce s databází v Androidu. Třída EventsDbHelper obsahuje funkce, které souvisejí

s ukládáním, vyhledáváním a mazáním přihlášených událostí. Pro komunikaci

s databází je použit objekt SQLiteDatabase, který lze získat pomocí volání metod

getReadableDatabase, respektive getWritableDatabase, podle toho, zda bude

z databáze čteno, respektive do databáze zapisováno. Objekt SQLiteDatabase

obsahuje metody, umožňující provádět dotazy a příkazy do databáze. V ukázce kódu

je část třídy, která vytváří a upgraduje databázi pomocí delegování na metody, které

vytvářejí a mažou tabulky pomocí standardních SQL příkazů.

class EventsDbHelper(val context: Context) :

SQLiteOpenHelper(context, "OpenDaysFim.db", null, 1) {

override fun onCreate(db: SQLiteDatabase) {

createTables(db)

}

override fun onUpgrade(db: SQLiteDatabase,

oldVersion: Int,

newVersion: Int) {

dropCreateTables(db)

}

Page 51: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

43

Funkce findAll vrací všechny uživatelem přihlášené události uložené v databázi,

využívá pro to funkci query, která je vytvořena jako extension funkce a volá metodu

query ze třídy SQLiteDatabase s nahrazením nepotřebných parametrů za null.

Tato funkce vrací objekt Cursor, který je obdobou ResultSetu z Javy.

fun findAll() = readableDatabase.use {

with(EventConstants) {

it.query(

TableConstants.EVENTS,

getColumns()

).use { cursor ->

CursorIterable(cursor) {

Event.fromCursor(cursor)

}.toList()

}

}

}

Pro lepší práci s kurzorem a možnost použití metod určených pro práci s kolekcemi

v Kotlinu, je použita třída CursorIterable, do které je kurzor předán. Druhým

parametrem konstruktoru CursorIterable je lambda výraz, který převádí jeden

záznam v kurzoru na objekt. V tomto případě je použita funkce fromCursor

companion objektu třídy Event, která vrací instanci této třídy.

internal class CursorIterable<T>(val cursor: Cursor,

val transformer: (Cursor) -> T)

: Iterable<T> {

override fun iterator(): Iterator<T> =

CursorIterator(cursor, transformer)

}

internal class CursorIterator<T>(val cursor: Cursor,

val transformer: (Cursor) -> T)

: Iterator<T> {

override fun hasNext(): Boolean = !cursor.isLast

&& !cursor.isAfterLast

override fun next(): T {

cursor.moveToNext()

return transformer(cursor)

}

}

Třída Event slouží pro uložení informací o události a je obdobou třídy Event ze

serverové části, ale má navíc vlastnosti selectedStart a selectedEnd, do kterých

se ukládá, v jaký čas se reálně chce uživatel události zúčastnit. A má navíc companion

objekt s funkcí fromCursor.

Page 52: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

44

companion object {

fun fromCursor(cursor: Cursor): Event {

val event = Event(cursor.getInt(0),

cursor.getString(1),

Date(cursor.getLong(2)),

Date(cursor.getLong(3)),

cursor.getString(4),

cursor.getString(5))

event.selectedStart = Date(cursor.getLong(6))

event.selectedEnd = Date(cursor.getLong(7))

return event

}

}

V třídě EventsDbHelper jsou ještě funkce save a delete, které ukládají, respektive

mažou předaný objekt Event z databáze. Ukládání používá metodu

insertWithConflict, která umožňuje přepisování existujících záznamů i vkládání

nových. Metoda with je v tomto případě použita pro zkrácení zápisu přístupu ke

konstantám obsahujícím názvy sloupců v tabulce událostí.

fun save(event: Event) {

with(EventConstants) {

writableDatabase.use {

it.insertWithOnConflict(TableConstants.EVENTS,

null,

ContentValues().apply {

put(COL_ID, event.id)

put(COL_NAME, "${event.name}")

}, SQLiteDatabase.CONFLICT_REPLACE)

}

}

}

fun delete(event: Event) {

delete(event.id)

}

fun delete(eventId: Int) {

writableDatabase.use {

it.delete(TableConstants.EVENTS,

"${EventConstants.COL_ID} == ?",

arrayOf("$eventId"))

}

}

Dále je v aplikaci několik dalších aktivit, ale zde bude popsána pouze jedna, protože

ostatní jsou podobné a mají podobné funkce. Prezentují uživateli data z databáze za

použití výše popsaných tříd a přebírají od uživatele data a ukládají je do databáze.

Aktivita EventDetailActivity slouží pro zobrazení informací o jedné události

a umožňuje uživateli si událost uložit, aby byl upozorněn v nastavený čas.

Page 53: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

45

Tato třída má několik vlastností, ve kterých jsou uloženy instance objektů GUI, jimiž

nastavuje data a listenery. Ve funkci onCreate je inicializováno view a získán

předaný objekt Event, který je obalen třídou EventParcelable, která umožňuje jeho

serializaci. Potom jsou do prvků GUI nastaveny některé informace a pomocí funkce

doInBackground získá z databáze aktuální údaje pro nastavení časů, které jsou

v postExecutionAction listeneru nastaveny do GUI. Funkce doInBackgroud přebírá

jako parametr funkci, která má být vykonána v jiném vlákně. Potom vytvoří, spustí

a vrátí instanci AsyncTaskWithListener.

fun <T> doInBackground(function: () -> T) =

object : AsyncTaskWithListeners<Unit, Unit, T>() {

override fun doInBackground(vararg p0: Unit?): T {

return function()

}

}.execute() as AsyncTaskWithListeners<Unit, Unit, T>

dále jsou v metodě onCreate registrovány listenery tlačítek zajišťujících přidání

a odebrání události z vybraných.

override fun onCreate(savedInstanceState: Bundle?) {

val event = intent

.getParcelableExtra<EventParcelable?>("selectedEvent")!!.event

txtEventName.text = event.name

val task = doInBackground {

DbHelpers.get(EventsDbHelper::class).findById(event.id)

}

task.addPostExecuteAction {

if (it != null) {

event.selectedStart = it.selectedStart

event.selectedEnd = it.selectedEnd

}

}

addButton.setOnClickListener {

doInBackground {

DbHelpers.get(EventsDbHelper::class).save(event.apply {

event.selectedStart.fillFromTimePicker(startPicker)

event.selectedEnd.fillFromTimePicker(endPicker)

})

}

startActivity(Intent(this@EventDetailActivity,

EventsActivity::class.java))

}

}

Page 54: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

46

Obr. 3 Hlavní aktivita a detail události.

Zdroj: vlastní zpracování

Page 55: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

47

4 Shrnutí výsledků

Programovací jazyk Kotlin představuje dobrou alternativu k jazyku Java. Protože

obsahuje funkce, které umožňují stručnější a přehlednější zápis, přesto je však

kompatibilní s knihovnami určenými pro jazyk Java. Přitom si zachovává podstatu

a výhody statického typování. Velkou předností je přitom jeho kompatibilita s Javou

verze 1.6, protože velké množství uživatelů, zejména velké firmy, stále ještě

nepodporují novější verze Javy. Další výhodou je, že se snaží vést k lepšímu stylu

programování, například používání neměnných objektů, vyhýbání se použití null

reference a podobně. Díky podpoře lambda výrazů, je možné snadněji tvořit

znovupoužitelný kód, a také se vyhnout různým duplicitám v kódu, které vznikají,

pokud se podobný kód liší například v jednom volání funkce.

Aplikace pro podporu dne otevřených dveří umožňuje uchazeči lépe si zorganizovat

konající se události, a snadno si vyhledat informace, i když zrovna není připojen

k internetu, pokud už si je alespoň jednou stáhnul. Všechny informace je možné

snadno upravovat pomocí serverové aplikace, a to buď přes jednoduché GUI, nebo

pomocí REST API.

Page 56: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

48

5 Závěry a doporučení

V práci byl představen programovací jazyk Kotlin spolu s ukázkou jeho praktického

využití na jednoduché aplikaci a porovnání s Javou, programovacím jazykem, nad

kterým je Kotlin postaven a je s ním plně kompatibilní. I když se jedná o jazyk

poměrně nový, je již dostatečně vyzrálý a použitelný na vývoj všech druhů aplikací.

Jeho hlavní výhodou je přehlednost, moderní syntaxe, následování nejnovějších

trendů v programování a v neposlední řadě zpětná kompatibilita se staršími

verzemi Javy. Další předností je snadnost učení a to obzvlášť pro programátory,

kteří už mají znalosti dalších populárních programovacích jazyků, protože Kotlin

přebírá vlastnosti a syntaxi z ostatních moderních jazyků. Mezi užitečné vlastnosti,

které Kotlin přebírá z jiných jazyků, než z Javy patří například používání vlastností

namísto getterů a setterů, unchecked výjimky nebo přetěžování operátorů.

Protože je Kotlin staticky typovaný jazyk, lze dříve odhalovat chyby, které se objeví

už při kompilaci a ne až za běhu. Další výhodou je rychlost, která je vyšší než

u dynamicky typovaných jazyků, jako je například Groovy. Statická typovost přináší

ještě jednu výhodu – lepší podporu ze strany integrovaných vývojových prostředí,

které nemusí provádět složitou analýzu průchodu kódem pro automatické

doplňování kódu a odhalování chyb.

Na Kotlinu je založena příkladová aplikace, která řeší požadavky uchazečů

o studium při první návštěvě fakulty na dni otevřených dveří. Mezi její hlavní

výhody patří možnost zorganizovat si den otevřených dveří podle požadavků

uchazeče. Může si předem naplánovat, jakých událostí se kdy zúčastní a kdykoliv se

dostat k informacím, které by mohl potřebovat. Při vývoji aplikace se naplno

projevila síla zvolených nástrojů, které napomohly k rapidnímu vývoji této aplikace

ve velmi krátkém čase. Zejména framework Spring, který umožňuje vytvořit

rozsáhlou funkcionalitu pomocí jen několika málo řádků kódu, kde jazyk Kotlin dále

zjednodušuje tuto implementaci, díky přímé podpoře pro často opakované vzory

v programování. Jádrem celého vývojového cyklu se stalo vývojové prostředí IntelliJ

IDEA od společnosti JetBrains s.r.o., které nativně podporuje jak Kotlin, tak i ostatní

technologie využité při tvorbě aplikace, jmenovitě Android, Gradle, Mercurial,

Spring, Hibernate a JavaScript.

Page 57: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

49

Další možný výzkum by se mohl týkat návrhů na vylepšení jazyka Kotlin, adresovat

jeho současné limitace a diskutovat možné budoucí směřování jazyka. Samotná

aplikace pro podporu dne otevřených dveří by mohla obsahovat prvky rozšířené

reality, obzvláště využitelné u interaktivních panoramatických fotek učeben.

Takováto virtuální zkušenost by mohla uchazeče ještě více motivovat k rozhodnutí

se pro Fakultu informatiky a managementu.

Page 58: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

50

6 Seznam použité literatury

[1] BLOCH, Joshua. Effective Java. (2nd Edition). Upper Saddle River, NJ, USA.

Vydavatelství: Prentice Hall PTR, 2008. Počet stran: 384. ISBN: 0321356683

9780321356680.

[2] Classes and Inheritance – Kotlin Programming Language[online]. Dostupné na

World Wide Web: <https://kotlinlang.org/docs/reference/classes.html>

[3] Functions – Kotlin Programming Language[online]. Dostupné na World Wide

Web: <https://kotlinlang.org/docs/reference/functions.html>

[4] Properties and Fields – Kotlin Programming Language[online]. Dostupné na

World Wide Web: <https://kotlinlang.org/docs/reference/properties.html>

[5] Visibility Modifiers – Kotlin Programming Language[online]. Dostupné na

World Wide Web: <https://kotlinlang.org/docs/reference/visibility-

modifiers.html>

[6] Null Safety – Kotlin Programming Language[online]. Dostupné na World Wide

Web: <https://kotlinlang.org/docs/reference/null-safety.html>

[7] Data Classes – Kotlin Programming Language[online]. Dostupné na World Wide

Web: <https://kotlinlang.org/docs/reference/data-classes.html>

[8] Generics – Kotlin Programming Language[online]. Dostupné na World Wide

Web: <https://kotlinlang.org/docs/reference/generics.html>

[9] PECINOVSKÝ, Rudolf. Návrhové vzory. Vydavatelství: Computer Press,

Albatros Media a. s., 2007. Počet stran: 528. ISBN: 8025145107 9788025145104.

[10] Processing Data with Java SE 8 Streams, Part 1[online]. Dostupné na

World Wide Web: <http://www.oracle.com/technetwork/articles/java/ma14-java-

se-8-streams-2177646.html>

[11] Collections – Kotlin Programming Language[online]. Dostupné na World

Wide Web: <https://kotlinlang.org/docs/reference/collections.html>

[12] JOHNSON, Rod, et al. Professional Java Development with the Spring

Framework. Vydavatelství: John Wiley & Sons, 2005. Počet stran: 676. ISBN:

0471748943 9780471748946.

[13] BAUER, Christian, KING, Gavin. Hibernate in Action. Vydavatelství:

Manning, 2005. Počet stran: 408. ISBN: 193239415X, 9781932394153.

[14] Bobril – I – Getting Started - CodeProject[online]. Dostupné na World

Wide Web: < http://www.codeproject.com/Articles/1044425/Bobril-I-Getting-

Started>

Page 59: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

51

[15] Spring Data JPA – Reference Documentation[online]. Dostupné na World

Wide Web: <http://docs.spring.io/spring-data/jpa/docs/current/reference/html>

[16] Activites – Android Developers[online]. Dostupné na World Wide Web:

<http://developer.android.com/guide/components/activities.html>

Page 60: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

52

7 Přílohy

1) Ukázky dalších aktivit

Page 61: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

Příloha č. 1

Page 62: Univerzita Hradec Králové Fakulta informatiky a ... fileUniverzita Hradec Králové Fakulta informatiky a managementu Katedra informatiky a kvantitativních metod Aplikace pro podporu

Oskenované zadání práce