Java 8 vs Scala Lambdat bytecode tasolle

Java 8 julkaistiin pitkän odottelun jälkeen, ja viimein kieleen saatiin Lambda lausekkeet – sekä niitä hyödyntäviä rajapintoja aikaiseksi. Monessa muussa kielessä Lambdat ovat olleet jo pitkään, ja voidaan ehkä sanoa että Scala popularisoi ne. Jäin miettimään onko Java ja Scala lambdatoteutuksissa eroja bytecodetasolla, molemmat kun ovat käännettäviä kieliä. Törmäsin kiintoisaan artikkeliin:

 

http://www.takipiblog.com/2014/01/16/compiling-lambda-expressions-scala-vs-java-8/

 

Aikanaan kun Scala tehtiin, kieleen lisättiin monenlaisia rakenteita jotka toteutetaan aika rajulla bytecode teurastuksella. Esim. funktio ilman luokkaa ei ole Javassa ollut mahdollinen, joten Scalassa on luotu keinotekoisia luokkia jos johonkin väliin. Lopputulos on kielen osalta hieno ja innovatiivinen mutta bytecode tasolla vähän purkkaviritystä.

Java 8 toteuttaa Lambdat käyttäen Java 7:ssä lisättyä uutta bytecode tason piirrettä nimeltä invokevirtual. Näin vältetään purkkaviritykset väliin, ja funktio voidaan luoda sellaisenaan. Syntyvä bytecode on paljon mutkattomampaa. Suorituskyvystä ei artikkelissa puhuttu mutta voisin arvata että senkin suhteen Java 8 vetää pisteet kotiin.

Eli koska rakastan kovasti Scala kielellä koodailua – se tuntuu raikkaan keväiseltä hengähdykseltä – tässä toivelista Scala piirteille joita pitäisi ottaa kiinni:

– Sama optimointi: Käytetään Java 7 invokedynamic piirteitä

– Samantien voitaisiin toteuttaa vähäisemmät Java 7 Coin piirteet: binääriliteraalit, try-with-resources (AutoCloseable), alaviivat numeroiden ryhmittämiseen (tämä ei ehkä onnistu ollenkaan koska villikortti), jne.

Mainokset

Java 8, Eclipse, IntelliJ, Netbeans 8 – kaikki ulkona

Jep, eilen tuli Java 8 release valmiiksi ja jakoon,se lopullinen versio. Se jossa on lambdoja, oletustoteutuksia rajapinnoissa, kokoelmat pistetty uusiksi, päivämääränkäsittelyt pistetty taas kerran uusiksi.Se kauan odotettu vastine Scalalle, josta olen bloggaillut jo aiemmin.

Omalta osalta tässä ei ole mitään järisyttävää, Java 8 release candidatet ovat itsellä pyörineet tuotannossa kera Glassfishin ja MondoDB:n jo kuukausia, ja kehitysympäristössä se on ollut oletuksena pidempään. Kuitenkin vaikuttaa että ihmiset ovat olleet sormi  liipasimella jo hetken, sillä samalla siunaamalla paukahti release versioita kaikista suosituista IDE:istä, ja lukuisia artikkeleita kasiversiosta.

Eli nyt sitä saa. Ja tässä linkityksiä:

Java 8:
http://www.oracle.com/technetwork/java/javase/downloads/index.html

Netbeans 8:
https://netbeans.org/

Eclipse with Java 8 (Luna, Kepler with SR2):
https://www.eclipse.org/downloads/ ja https://wiki.eclipse.org/JDT/Eclipse_Java_8_Support_For_Kepler

IntelliJ IDEA 13.1:
http://www.jetbrains.com/idea/

Eli, kimppuun vain ja hauskaa pitämään! Tieturilla on tietysti myös koulutusta tarjolla, http://www.tieturi.fi/kurssit/kurssi.html?course=85000345

 

 

Java 8 : Se ei oo sun isoisän Java enää…

Jep, olen benchmarkannut aiemmin muita ohjelmointikieliä kuten Scala ja Groovy sillä miten paljon koodia vaatii tehdä rss-feedin lukija, joka esim. poimii sieltä title ja link tageja. Vanhalla Javalla koodaten – juu tuo vie hetkenkin verran. Ja sisältänee while tai for-silmukkaa ja imperatiivista koodia. Ei siinä mitään, koodaushan on kivaa.

Tässä java 8 versio RSS lukijasta joka lukee ja tulostaa Tieturin blogin otsikoita ja linkkejä – käytössä Streamit ja Lambdat:

new BufferedReader(
  new URLReader(new URL("http://feeds.feedburner.com/tieturi")))
    .lines()
    .filter(a -> a.contains("<title>") || a.contains("<link>"))
    .forEach(a -> System.out.println(a));

Jep, one-lineri. Valmistaudu näkemään tällaista koodia enemmänkin.. 😉

 

Tykkäätkö erityisesti hitaasta, monimutkaisesta ja vaikeasta? Älä missään nimessä siirry Java 8:aan!

Jep, provosoivaan otsikkoon vähän sisältöä. Olen jo vuoden päivät opiskellut Java 8 version tulevia muutoksia, kyseessä on tosiaan iso pläjäys joka koodaajaa hyödyntävää tavaraa. Ne summautuvat yhteen tehokkuudeksi – monissa paikoissa voi nyt tehdä koodaustyöt siten että koodirivejä syntyy vähemmän – koodi on luettavampaa ja näin ollen kalliita huolimattomuusvirheitä on vähemmän – ja kaiken lisäksi uudella tapaa tehty koodi voi skaalautua moniydinprosessoireissa ihan uudella tapaa. Turha odotella paria vuotta kun kaikesta tästä voi nauttia NYT. Koodaajille tätä ei ole tarpeen paljoa perustella, mutta koko projektin kannalta näkyy jo selvää rahaa kun siirtyy käyttämään fiksumpia uusin piirtein varustettuja alustoja. Jos pyörität vielä Java versioita 5 tai 6, on peiliin katsomisen aika. 😉

Java 1.8 julkaistaan näillä näkymin 18.3. lopullisena versiona. Siitä on jo toimivaa Release Candidate versiota tarjolla. Jokainen kehitysympäristö tukee jo enemmän tai vähemmän sen käyttöä. Ja mikä tärkeintä, Tieturilla on tarjolla heti koulutusta jossa saat kickstartin uusiin piirteisiin ohjatusti ja vinkkejä parhaista käytännöistä ja myös sudenkuopista joita voi tulla vastaan.

Java 8 uudet piirteet (Huhti ja toukokuussa toteutus):
http://www.tieturi.fi/kurssit/kurssi.html?course=85000345

Tärkeimmät tulossa olevat uutuudet ovat tietysti Lambdat, joka toimii kevyenä porttihuumeena, ja sitten Streamit, joissa piilee se kova kama. Sen ohella vietellään kovasti kohden funktionaalista ohjelmointia joka on testattavampaa ja skaalautuvampaa. Vanha kunnon isoisän Javahan alkaa tässä näyttämään ihan Scalalta! Kun soppaan lisätään vielä uusi päivämääräkirjasto, joka on Jodatimen mukaan tehty ja ensimmäistä kertaa alkaa olla kohtuullisen mehukas – on aika painavat syyt päivittää itsensä ja ympäristönsä ASAP.

Tein vähän mallikoodia omaan käyttöön testatakseni piirteitä. Lähtötilanteena oli käydä läpi vähän tietokannastamme löytyvää kurssitarjontaa, ja tehdä siihen erilaisia hakuja. Otin tahallisesti laajan otoksen kaikesta mitä löytyi, joten datan laadussa on hyvin tyypillisiä heikkouksia, mm. puuttuvia kenttiä, ja tein siihen liittyvää perinteistä hyvin rumaa mutta myös hyvin tyypillistä imperatiivista koodia. Kuvaavaa on että koodin monimutkaisuuden vuoksi ehdin tehdä parikymmentä kertaa jonkin virheen joka testatessa kävi ilmi. Huolimattomuusvirheet voivat isommassa projektissa tulla äkkiä hyvin kalliiksi etenkin jos testaus ei ole automatisoitua, kattavaa, säännöllistä  ja nopeaa. Lähdin alunperin testailemaan suorituskykyä, mutta matkalla löytyikin muuta. Katsotaanpa mitä tapahtui.

Lähtötilanne: Kannasta on haettu tauludataa, joka on talletettu JSON olioihin. Pääohjelma lataa ne levyltä, ja prosessoi ne läpi, tulostaen lopuksi käsitellyt tiedot. Pääohjelma on tämän näköinen:

public static void main(String[] args)
    throws IOException, JAXBException {
  List<Course> courses = loadCourses("test.json");
  long begin = System.nanoTime();
  List<Course> validPublicTrainings = filterAndFixCourses(courses);
  long end = System.nanoTime();

  // Print values out to control how it works 
  for (Course course : validPublicTrainings) { 
    System.out.println(course);
  }

  System.out.println("Number of filtered courses: " 
    + validPublicTrainings.size()); 
  System.out.println("Time spent: " + (end - begin) / 1000000.0 
    + "ms"); 
}

Melko yksinkertaista tavaraa, siis. Kaikki mehukas tapahtuu filterAndFixCourses() funktiossa, sen tehtävä on suodattaa kurssit jotka ovat julkisia, valideja, ja pistää ne päivämäärän mukaan järjestykseen. Ongelmia tuottaa, että osassa dataa arvoja puuttuu ja ne ovat null-arvoj (Kaiken tämän voisi tehdä kannassa mutta tämä ei ole kantakoodauksen artikkeli 😉

Tältä näyttää perinteiseen imperatiiviseen tyyliin kirjoitettu varsin ruma ja optimoimaton (mutta myös varsin tyypillinen) esimerkki:

public static List<Course> filterAndFixCourses(
    List<Course> courseList) {
  // Get all trainings that are public and valid
  List<Course> validPublicTrainings = new ArrayList<>();
  for (Course course : courseList) {
    if (course.getType() != null && course.getType() == 3 
        && course.getStatus() != null && course.getStatus() == 3) {
      if (course.getBeginDate() != null 
          && course.getEndDate() != null) {
        // Fix dates by adding +10 hours, 36000ms
        course.setBeginDate(new Date(
           course.getBeginDate().getTime() + 36000));
        course.setEndDate(new Date(
           course.getEndDate().getTime() + 36000));
      }
     validPublicTrainings.add(course);
    }
  }

  // Sort by begindate and enddate
  // Descending order for dates, meaning that newest dates go first
  // Null values go to bottom of list
  Comparator<Course> c = new Comparator<Course>() {
    @Override
      public int compare(Course o1, Course o2) {
        if (o1.getBeginDate() == null 
            && o2.getBeginDate() == null) {
          return 0;
        } else if (o1.getBeginDate() == null) {
          return 1;
        } else if (o2.getBeginDate() == null) {
          return -1;
        } else if (o1.getEndDate() == null 
            && o2.getEndDate() == null) {
          return 0;
        } else if (o1.getEndDate() == null) {
          return 1;
    } else if (o2.getEndDate() == null) {
      return -1;
    } else if (o1.getBeginDate().before(o2.getBeginDate())) {
      return 1;
    } else if (o1.getBeginDate().after(o2.getBeginDate())) {
      return -1;
    } else if (o1.getEndDate().before(o2.getEndDate())) {
      return 1;
    } else if (o1.getEndDate().after(o2.getEndDate())) {
      return -1;
    } else {
      return 0;
    }
   }
  };
  Collections.sort(validPublicTrainings, c);
  return validPublicTrainings;
}

Rumaa? Jep, elämä on. Tuota voisi stilisoida moninkin tavoin mutta totuus on, että tuollaista koodia löytyy tuotantojärjestelmistä ympäri maailman. Kuten Venkat Subramaniam sanoi luennoissaan, kun tuota kirjoittelee päivän, tuntee olonsa likaiseksi ja kaipaa suihkua.

Miltäpä tämä sitten näyttäisi Java 8 avulla? Kokeillaanpa uudestaan. Ei mikromanageroida ulkopuolelta mitä pitäisi tehdä, vaan käytetään Lambdoja ja Streameja, ja annetaan algoritmit suoraan Streamille, näin:

public static List<Course> filterAndFixCourses(
    List<Course> courseList) {
  return courseList.stream()
    .filter(c -> c.getType() != null)
    .filter(c -> c.getType() == 3)
    .filter(c -> c.getStatus() != null)
    .filter(c -> c.getStatus() == 3)
    .sorted((o1, o2) -> {
       if (o1.getBeginDate() == null 
            && o2.getBeginDate() == null) {
         return 0;
       } else if (o1.getBeginDate() == null) {
         return 1;
       } else if (o2.getBeginDate() == null) {
         return -1;
       } else if (o1.getEndDate() == null 
           && o2.getEndDate() == null) {
         return 0;
       } else if (o1.getEndDate() == null) {
         return 1;
       } else if (o2.getEndDate() == null) {
         return -1;
       } else if (o1.getBeginDate().before(o2.getBeginDate())) {
         return 1;
       } else if (o1.getBeginDate().after(o2.getBeginDate())) {
         return -1;
       } else if (o1.getEndDate().before(o2.getEndDate())) {
         return 1;
       } else if (o1.getEndDate().after(o2.getEndDate())) {
         return -1;
       } else {
         return 0;
       }
     })
     .map((c) -> fixDate(c))
     .collect(Collectors.toList());
}

Parempi? Mmmhmm… Tavallaan, imperatiivisen tyylin mikromanagerointi on poistunut, ja voisi sanoa että koodi on alkuun jopa ymmärrettävämpää. Mutta monimutkaiset asiat ovat edelleen monimutkaisia ja sotkuisia, vaikeasti ymmärrettäviä. Mutta meillä on käsissämme funktionaalinen ohjelmointityyli. Mitäpä jos.. käyttäisimme funktioita hoitamaan likaiset työt? Sama uusiksi funktioreferensseillä Java 8 tapaan:

public static List<Course> filterAndFixCourses(
    List<Course> courseList) {
  return courseList.stream()
    .filter(App::typeThreeIsValid)
    .filter(App::statusThreeIsValid)
    .sorted(App::sortByDate)
    .map(App::fixDate)
    .collect(Collectors.toList());
}

Aaaaaivan! Koodirivien määrä rupsahti 53:sta kuuteen – tai jos tarkkoja ollaan niin yhteen, tuohan on kaikki samaa return lauseketta eli one-lineri. Tietysti osa koodia siirtyi vain funktioihin joka aina kannattaisi tehdä. Kuten yllä näkyy, hyvä idea nimetä funktiot dokumentatiiviseen tapaan, vähän kuin hyvissä yksikkötesteissä.

Mutta luettavuuden voisi väittää parantuneen, kun ylläolevaa koodia lukee, sitä voi jopa ymmärtää. Kenties jopa kuukauden päästä palatessaan asiaan.

Mitäpä tapahtui suorituskyvylle? Kellotin vähän millisekunteja. Imperatiivinen koodiesimerkki rusikoi 100 000 tietueen massaa n. 27ms verran. Funktionaalinen Java 8 versio käytti samaan aikaa n. 54ms. Hetki, miten tässä näin kävi? Eikö tämän pitänyt olla nopeampaa? No ei. Skaalautuvampaa, kyllä. Nopeushävikkiä voi syntyä esim. immutable-olioiden kopioinnista, ja toisaalta tässä ovat operaatiot niin nopeita että ylimääräisten askelien overhead jää suuremmaksi kuin mikään tehosäästö.

Mutta, tämänhän piti skaalautua mukavasti moniydinprosessoreille. Tässä koneessani on niitä 8, ajetaanpa testi uusiksi tällä koodilla:

// Best version of Java 8 features with parallel execution
 public static List<Course> filterAndFixCourses(
    List<Course> courseList) {
    return courseList.parallelStream()
       .filter(App::typeThreeIsValid)
       .filter(App::statusThreeIsValid)
       .sorted(App::sortByDate)
       .map(App::fixDate)
       .collect(Collectors.toList());
}

Kuten huomaat, ainoa mitä piti muuttaa oli korvata stream() parallelStream() kutsulla. Nyt taustalla käytetään Fork&Joinia ja hajoitetaan ja hallitaan operaatioita, ajaen ne rinnakkain. Mitenkäs suuri nopeushyöty saadaan tästä?

Turpiin tuli. Nyt aikaa meni 90ms, eli lähes neljä kertaa hitaampaa. Mikä meni vikaan?

Kuten aiemminkin, overhead iski. Thread poolin pystytys ja töiden skedulointi vie suoritusaikaa. Tässä esimerkissä laskennallinen tehtävä on edelleen triviaalin nopea ja siksi sen tekeminen rinnakkain ei hyödytä tarpeeksi. Eli jos nopeutta hakee, tämä kannattaisi edelleen tehdä mutkattomammin imperatiivisesti. Ytimien lisääminenkään tuskin auttaisi, koska overhead.

Mutta.. entäpä jos työ olisi raskaampaa.. Nykyään dataa haetaan usein verkon yli, tai tietokannasta, useissa erissä. Edellämainitun koodin ongelmana alkoi olemaan muistin riittäminen jos tietueita olisi vaikka miljoona. Looginen ratkaisu olisi hakea sitä sopivammissa viipaleissa kannasta tai verkon yli.

Entä jos siinä menisi esim. 10ms per kierros aikaa? Simuloidaan koodiin pientä verkkoviivettä, näin:

try {
 Thread.sleep(10);
 } catch (InterruptedException ex) { }

Joka kierroksella menee hetki aikaa, joko laskenta on raskasta tai haetaan jotain verkon yli tai kannasta. 18793 tehtävää (viive on vain suodatetuilla alkioilla), 10ms viivettä per tehtävä. Nyt tilanne muuttuu, imperatiiviselta koodilta kestää: 194 045ms tehdä työ. Ei kovin suuri yllätys, 100k kertaa 10 on miljoona. Entä miten suoriutuu parallel Stream Java 8 versio? Suoritusaika on 25272ms, eli imperatiivisen koodin aika jaettuna ytimien määrällä plus overhead. Kahdeksan ytimen kone ajaa operaatiot lähes 8x nopeammin, ja skaalautuu jos ytimiä onkin 16, 32, jne..

Yhteenveto: Java 8 uudet piirteet ovat uutta arsenaalia ohjelmoijan työkalupakissa. Jo selkeyden ja ongelmanratkaisun välineinä niitä ei ole varaa olla käyttämättä. Ne eivät automaattisesti ole ylivoimaisia suoritusnopeudessa – mutta tehtävissä joissa rinnakkaisuudesta voi olla hyötyä, ne tuovat ylivertaista ja ohjelmoijalle helppoa skaalautuvuutta. Ja vielä on yksi syy lisää opiskella tämä NYT: Ellet opiskele, pian voi pamahtaa silmille Java-koodia jota et enää pysty ymmärtämään. Muistatko miten kävi Java 5 kohdalla? 😉

Pari tärppiä vielä loppuun. Streamien debuggaus voi olla hankalaa – ja tuki vielä huteraa kehitysvälineissä. Apuun tulee peek()-funktio, jolla voit kurkata prosessoinnin sisään. Esim. näin:

return courseList.parallelStream()
  .filter(App::typeThreeIsValid)
  .filter(App::statusThreeIsValid)
  .sorted(App::sortByDate)
  .peek(System.out::println)
  .map(App::fixDate)
  .collect(Collectors.toList());

Ei siis hullumpaa uudistusta. Matkaa on vielä Scalan hienouksiin mutta nyt meillä on jotain käyttökelpoista joka taas inspiroi koodaamista vuosiksi eteenpäin.

JFokus 2014 loppuraportti

Osallistuin tänä vuonna JFokukseen sekä kuuntelijana että myös näyttelyn puolella yläkerrassa. Meillä oli täkynä LeapMotion laite ja se herättikin huomiota. Olen jo aiemmin bloggaillut sen koodailusta, joka on erityisen hauskaa Java 8 Lambdoilla ja JavaFX:llä.

With Great Power Comes Great Responsibility

Seminaarissa näytti toistuvan jo viime vuoden San Fransciscon JavaOne seminaarin teemat – osittain jopa samat luennot, slidet ja vitsit. Kuumia pääteemoja näyttää tälläkin vuodella olevan:

  • Tuleva Java 8 ja sen kaikki muutokset,mutta erityisesti Lambdat
  • Internet of Things muodossa: Halpoja laitteita jotka mittaavat/ohjaavat, helposti koodattavissa
  • Reaktiiviset käyttöliittymät ja JavaScriptin uusi tuleminen

Itse innostuin myös uudella tavalla Stream API:sta. Kuten Venkat Subramaniam sanoi jo JavaOne seminaarissa – Lambdat ovat porttihuume, ja Streamit ovat se kova huume johon ne johtavat, ja joista on vaikeampaa päästä eroon. Streameissä manifestoituu kaikki se uusi hyvä (ja huono) mitä Java versio 8 tuo mukanaan, ja ne tuovat valtavasti voimaa koodaajan arkeen.Ja niitä voi alkaa käyttämään heti. Tätä heijastaen Tieturin Java 8 kurssilla Streamien osuutta tullaan syventämään.

Venkat puhuu Lambdojen ja Streamien voimasta

Java 8 julkaisu on 18. Marraskuuta – oletko valmis? Release Candidate 1 on saatavilla heti, ja paljon tietoa. Uusien piirteiden myötä tulee uskomattomasti voimaa – tehokkuuta ja koodauksen selkeyttä – mutta myös vastuuta ja sudenkuoppia. Paras alkaa treenaamaan, montaa vuotta näitä ei voi väistellä enää. Itse ajan RC1 versiota Java 8:sta Raspissani ja muutama muukin päivittäisessä käytössä oleva työkalu on suoraan JDK 8:lla rakennettu.

Java 1.8.0 RC1 in Raspberry Pi

Mielenkiintoista on myös,että koska tämä ei ole Oraclen seminaari, myös Google näkyi täällä, ja osallistuin mielenkiintoiseen esitykseen tänä vuonna julkaistavasta Google Glass-älylaseista. Pär Sikö piti hyvän esityksen niiden ohjelmoinnista. Ne ovat näemmä käytössä jo mm. palomiehellä ja parillakin lääkärillä työssä, ja koodausmahdollisuudet sen kun vain lisääntyvät.Uuden GTK rajapinnan myötä monetkin Android 4:lle kehitetyt sovellukset ovat siirrettävissä uuteen käyttöön – kunhan vain käyttöliittymän rajoitukset huomioidaan.

Google Glass - What's Best In Life?

Tuli itsekin kokeiltua niitä – lasit reagoivat kiitettävän nopeasti komentoihin ja näyttö on kirkas,selkeä ja hieno. Käyttöliittymä saa tottumattoman takeltelemaan, etenkin kun jotain menee pieleen. Mielenkiintoista miten olemassaolevat Android kehitysvälineet – ja myös osaaminen – sopivat suoraan tälle.

JFokus

Ruotsin JFokus on skandinaavisen softakehittäjän kannalta mielenkiintoinen ja helposti saatavilla oleva seminaari jota voin lämpimästi suositella.

Liikettä, Lambdaa ja JavaFX:ää

Ei kai sitä oikea propellihattu olisi jos ei joskus kokeilisi epävakailla beta-versioilla koodata. Niinpä löysin itseni tilanteesta johon kuului:

-LeapMotion kontrolleri joka mahdollistaa ohjauksen ilmaan tehtävillä eleillä kosketuksen sijasta. Jep, kuten Minority Reportissa 😉

– Java 8 early access versio

– Netbeans 7.4

– JavaFX 8 Developer Preview

Miksi juuri tämä setti? No mehukkaat 3D rajapinnat tulevat vasta JavaFX 8 versiossa joka ei ole vielä valmis. Tällä hetkellä paras alusta kokeilla sitä on Java 8. Jossa sattuu tulemaan myös maukkaat Lambda Expressionit mukana. Netbeans 7.4 tukee jo Java 8 versiota. Kuten myös erinomaisesti HTML5+JavaScript koodausta.

LeapMotion kontrolleri

Joka tapauksessa, LeapMotion kytkeminen tapahtuu USB väylän kautta, ja Java alkaa juttelemaan sen kanssa kun sovellukseen liitetään leapmotion.jar kirjasto, sekä käyttöjärjestelmäkohtaisia dll:iä pari. Tämän jälkeen rajapinta antaa mukavasti tietoa siitä mitä mokkula havainnoi n. metrin pallossa ympärillään. Sen verran tarkkaa on että esim. sormien lukumäärä voidaan lukea, erilaiset eleet ja tietysti käsien ja sormien koordinaatit kolmiulotteisessa avaruudessa.

JavaFX 8 käyttöönotto vaati hieman enemmän vaivaa, mutta selvimmin homma toimi tosiaan asentamalla Java 8 early access, jossa nuo JavaFX 8 kirjastot tulevat suoraan mukana, kytkemällä se Netbeansiin, ja valitsemalla Maven arkkityypiksi zenjava javafx maven plugineineen kaikkineen. Samaan Maven pom:iin tietysti riippuvuudet LeapMotion Java kirjastoihin (piti asentaa paikalliseen repositoryyn koska maven centralista ei vielä löydy). Ja lopulta vielä Netbeans run-parametreja muokaten sen verran että Leapmotion .dll tiedostot tulevat mukaan.

LeapFX8a

Mutta nyt toimii. Ajattelin kirjoitella yksittäisen prototyypin ensin selvittääkseni mihin laite pystyy ja mitä tietoa sieltä irtoaa. Pistin kolmiulotteiseen avaruuteen Tieturi-logolla varustetun kuulan. Kytkin sen LeapMotion kontrollerin koordinaatistoon käyttäen JavaFX Property Binding metodia. Tässä yhteydessä Java 8 Lambdat olivat maukkaita: JavaFX:ssähän ei mm. saa koskea UI:hin edes property bindingin kautta ilman että operaatio ajetaan osana UI säiettä. Muuten tulee poikkeus. Tässä esimerkkisnippet Lambdoista ja Leapmotion rajapinnasta:

if (!frame.hands().isEmpty()) {
    Hand hand = frame.hands().get(0);
    Vector vPos = hand.palmPosition();
    Platform.runLater(() -> {
        positionX.set(v.getX());
        positionY.set(v.getY());
        positionZ.set(v.getZ());
        rotateValue.set(hand.direction().yaw());
    });
}


Yep, ja positionX, Y ja Z ovat siis JavaFX Bound Property arvoja, joten kun muutan niitä, kuulan ko koordinaatit muuttuvat automaattisesti sen mukaan. Jouduin harrastamaan pientä aivojen rassausta koska Java koordinaatisto alkaa vasemmasta ylänurkasta xy-suunnassa kun taas leapmotion normaalisti vasemmasta alanurkasta. Miten hoidetaan tämä bound propertyillä? Näin:

sphere.translateYProperty().bind(positionY.negate().add(SCREEN_SIZE_Y));

Ja tietysti sama z-koordinaatille, x meni onneksi ihan suoraan. Nyt kuula liikkuu kolmiulotteisessa avaruudessa kun käteni, loittonee, tulee lähemmäs, menee ylös, alas, vasemmalle, oikealle, viistoon, etc. Tottakai pakkaan voi lisätä muitakin asioita kuten ranteen kiertoa, sormien lukumäärää, nopeutta akselilla, jne. Mitä iloa on sitten animoida tekstuurimäpättyä kuulaa 3d-avaruudessa?

Itselleni suurtakin, hauska projekti vaihteeksi kaiken yritys-transaktio-ejb-vakavuuden välillä. Mutta seuraava steppi on kytkeä tuota käyttöön oikeiden sovellusten käyttöiittymiin. Ja kun lempiaiheenani on viime aikoina ollut Business Intelligence, reaaliaikametriikka raportoinnin sijaan, Big Data ja Fast Data, ja oikea ja luotettava tieto liiketoiminnan käyttöön, arvaahan sen mitä siitä syntyy… 😉

Tämä on taas yksi niitä asioita joissa video kertoo enemmän kuin kuva.


KÄYTTÄJÄT ANSAITSEVAT PAREMPIA JA INTUITIIVISEMPIA KÄYTTÖLIITTYMIÄ

JavaOne 2013 – keskiviikko

JavaOne seminaari ja keskiviikkoon asti ollaan päästy. Hauskasti aina meno vähän tasoittuu kun homma etenee, osa lähtee pois jo aikaisemmin, ja muutenkin tuntuu että jäljelle jääneet ovat enemmän keskittyneet sisältöön. Sitä heijastaen luvassa on enemmän keskittyneitä luentoja yleisten linjausten sijasta.

2013-09-25 09.49.19

Aamuksi buukkaamani luento Lambda Expression aiheesta oli napakymppi: Luennoija Venkat Subramamiam on täsmällisten tietojensa ohella erinomainen ja hauska luennoija. Esitys muistutti puolittain hyvää standup-showta, mutta koska kerrotut anekdootit ja metaforat liittyivät tiiviisti aiheeseen, ne ylläpitivät vain parempaa keskittymistä ja parempaa asioiden muistamista. Mahtavia lainauksia olisi riittänyt pilvin pimein aiheista. Mutta tässä tiivistelmää:

– Ohjelmoijan kannattaa opiskella useita kieliä, niin montaa kuin mahdollista. Ei vaihtaakseen vanhaa uuteen trendien mukaan, vaan koodatakseen vanhaa paremmin. Ei kannata siis odotella Java 8 versiota, vaan mars oppimaan Scalaa ja Groovya siis! 😉

– Imperatiivinen koodi ja ’mutable state’ eli muuttuva tila ovat äärimmäisen bugiherkkiä. Kun olioihin vaikutetaan ulkopuolelta koodi keskittyy tekemään kaikkea mikromanageroinnin tasolla. Se on kuin yrittäisi ulkoiluttaa vastahakoista koiraa ja joutuisi siirtämään sen jalkoja yksi kerrallaan jotta se liikkuisi. Koodin luettavuus kärsii koska koodi tekee kaikkea muuta kuin varsinaista ongelmanratkaisua. Ongelmat löytyvät testauksen kautta.

– Funktionalinen koodi kuten Scalassa ja tulevassa Java 8 versiossa on ytimekkäämpää, mutta ennen muuta luettavampaa ja ymmärrettävämpää. Edelliseen esimerkkiin liittyen se on kuin heittäisi koiralle pallon. Koira liikkuu itsestään pallon perään, se tietää mitä tehdä ja miten jalat liikkuvat parhaiten.  Funktionaalisessa mallissa on mahdollista optimoida suoritusta, ja vaihdella algoritmeja tilanteen mukaan.

– Joka johtaakin rinnakkaiseen suoritukseen. Venkat näytti demon jossa ensin haettiin Amazon web serviceistä osaketietoja ja etsittiin kalleinta alle 500 dollarin osaketta imperatiivisella logiikalla: Koodirivejä n. 25 ja suoritusaika n. 75 sekuntia. Sitten tehtiin sama homma funktionaalisesti. Koodirivejä 1 (no viitisen vaihetta mutta vain yksi puolipiste 😉 – suoritusaika n. puolet, koska asiat tehtiin lazy evaluation – tyyliin fiksusti. Lopuksi muutettiin yksi kohta: parStream(), ja saatiin rinnakkainen suoritus fiksusti automatisoiden, toimintavarmasti. Aikaa kului n. 10 sekuntia. Pääpointti on, että funktionaalista suoritusta voi optimoida, olio tietää aina itse parhaiten miten toimia (tai sen tulisi).

– Rinnakkainen käsittely on toteutettu Java 7:stä tutulla Fork&Join kehyksellä jota ei enää tarvitse koodata matalalla tasolla itse – jos haluaa ajaa kokoelmien kanssa operaatioita rinnakkain – ja kukapa haluaisi?

Lambdat tarkassa syynissä

Koodiesimerkki Venkat Subramamilta: Hae kallein alle 500 dollarin osake (käyttäen apuluokkaa joka tekee web service kutsut ja vertailut):

public static void findStockImperative(List<String> symbols) {
 List<StockInfo> stockPrices = new ArrayList<>();

 for(String ticker : symbols) {
 stockPrices.add(StockUtil.getPrice(ticker));
 }
List<StockInfo> stocksLessThan500 = new ArrayList<>();
 for(StockInfo stockInfo : stockPrices) {
 if(StockUtil.isPriceLessThan(500).test(stockInfo))
 stocksLessThan500.add(stockInfo);
 }

 StockInfo highPriced = new StockInfo("", 0.0);
 for(StockInfo stockInfo : stocksLessThan500) {
 highPriced = StockUtil.pickHigh(highPriced, stockInfo);
 }

 System.out.println(highPriced);

 }

Toinen koodiesimerkki: Sama funktionaalisesti Java SE 8 piirteillä (mapreduce):

public static void findStockDeclarative(Stream<String> tickers) {
 StockInfo highPriced = 
     tickers
         .map(StockUtil::getPrice)
         .filter(StockUtil.isPriceLessThan(500))
         .reduce(new StockInfo("", 0.0), StockUtil::pickHigh);
 System.out.println(highPriced);
 }

Ja näin päästiin suoritusajassa 70 sekunnista 10 sekunnin paikkeille. Mahtava luennoija tämä kaveri joka tapauksessa, voin lämpimästi suositella kuuntelemista jos tilaisuus tulee. Tässä muutama lainaus (eivät ole sanantarkkoja eivätkä tee oikeutta originaaleille mutta jakamisen arvoisia niille jotka eivät esitystä kuulleet):

– Mitä tarkoittaa rinnakkainen ohjelmointi Javassa? Se tarkoittaa että koodaaja koodaa samaan aikaan kaameaa möykkykoodia ja samalla päivittää CV:tään kuntoon…

– Miltä tuntuu koodata imperatiivisesti muuttuvalla tilalla? Siitä tulee likainen olo, on inhottavaa mennä työpäivän jälkeen kotiin.

– Imperatiivinen kokoelmaluokkien ohjelmointi on suunnittelumalli – nimeltään Self-inflicted wound

– Java koodaajat eivät koskaan kirjoita typerää koodia – he koodaavat kehitysympäristöjä jotka kirjoittavat typerää koodia.

– Muuttuva tila on NIIIIN 1900-lukua!

– Lazy lausutaan efficient!

Luonnollisesti, disclaimer perään: Rinnakkaisuus ja etenkin helppo ja toimintavarma sellainen on joskus mahtavan hieno ratkaisu. Toisinaan taas ei, se ei ole maaginen hopealuoti joka aina ja joka paikassa olisi oikea ratkaisu. Samoin funktionaalinen ohjelmointi, se ei ole joka paikassa aina oikea tapa toimia, vaikka usein on. Järjenkäyttö on sallittua, imperatiiviselle ja peräkkäiselle ohjelmoinnille on edelleen paikkansa. Nyt on vain vaihtoehtoja. Sekä Scalassa että Javassa on vahvasti yksi jalka oliomaailmassa, yksi jalka funktionaalisessa. Ne antavat ohjelmoijille mahtavan työkalupakin.

Muutenkin tämän otin lambda-päivän kannalta, esitykset ja labrat joihin osallistuin käsittelevät asiaa pintaa syvemmältä eri kanteilta. Osallistuin Hands-On-Lab työpajaan jossa ratkaistiin valmiita yksikkötestirunkoja Lambdoilla – käyttäen Java 8 esiversiota ja Netbeansiä jotka ovat vielä uudempia kuin tämän hetken developer preview 😉 Teknisistä ongelmista selvittyä pistettiin Lambdoille vauhtia, ja tehtiin uskomattomia asioita kokoelmila. Siinä missä itse Lambdat ovat selkeää ja peräti esteettistä koodia, täytyy sanoa että Java 8 Collection rajapinnat alkavat näyttää diabolisilta. Se on kuin kännissä koodattua Scalaa! Se siitä selkeälukuisuudesta. Silti, tehokkuusmielessä tapahtuu uskomattomia asioita, ja kun pääsee jyvälle miten asiat toimivat, Collections stream() api on aika rautaa. Vastaava koodi imperatiivisella ohjelmoinnilla voisi edelleen näyttää pahalta.