blog.smart-java.nl
Ordina J-Technologies – Java Blog

Archief ‘Java language’ categorie




java.util.Calendar.getActualMaximum returns strange results

Door: Peter Schuler, 20 January 2010

At the end of last year I encountered something odd in the java.util.Calendar. Now is odd behavior nothing to be surprised of in the Java Calendar but this particular oddness was really hard to spot.

I will therefore share it with you.

The code

Let’s first look a some code dealing with getting the last day of the month:

public class CalendarTest {
  public static void main(String[] args) {
    Calendar c = Calendar.getInstance();
    c.set(Calendar.MONTH, Calendar.FEBRUARY);
    int maxDayOfMonth = c.getActualMaximum(Calendar.DAY_OF_MONTH);

    System.out.println("last day of month = " + maxDayOfMonth);
  }
}

Please read the java doc for Calendar.getActualMaximum():

Returns the maximum value that the specified calendar field could have, given the time value of this Calendar. For example, the actual maximum value of the MONTH field is 12 in some years, and 13 in other years in the Hebrew calendar system.

So the code above can print two different values right …? 28 or 29 depending on the whether this year is a leap year. As you could have guessed that is another unexpected possibility. I can also print 31. Yes… really.

I all depends on the date on which this code is executed.

The problem is that Calendar.getInstance() will return a Calendar filled with the current date/time. Let’s assume the above code runs on the last day of January. The call to getInstance() will return 31-01-2009. The next step puts the month to February. This will result in a overflow as 31-02-2009 is invalid. Because of this Calendar will move the date to 3-03-2000. And March has 31 days.

There is a bug report (status: Closed, Not a Defect) in the SDN which told me that the observed behavior was correct. I was not the first person surprised by this and I’m guessing I will not be the last.

I was …

  1. … lucky I wrote a decent unit test.
  2. … lucky to be running said unit test om 31 December.
  3. … unlucky for having to work on the last day of the year.

Otherwise the bug I created would properly have slipped through the QA cycles and would have ended up in production. There it would only be visible on the last three days of every month if someone would specify a date in February.

What about lenient?
The Calendar.setLenient function does protect against invalid input. Let’s quote the the Calendar java doc about Leniency:

Leniency
Calendar has two modes for interpreting the calendar fields, lenient and non-lenient. When a Calendar is in lenient mode, it accepts a wider range of calendar field values than it produces. When a Calendar recomputes calendar field values for return by get(), all of the calendar fields are normalized. For example, a lenient GregorianCalendar interprets MONTH == JANUARY, DAY_OF_MONTH == 32 as February 1.

When a Calendar is in non-lenient mode, it throws an exception if there is any inconsistency in its calendar fields. For example, a GregorianCalendar always produces DAY_OF_MONTH values between 1 and the length of the month. A non-lenient GregorianCalendar throws an exception upon calculating its time or calendar field values if any out-of-range field value has been set.

So lenient does protect against the programmer/user creating a invalid date in a way that the following code will result in an exception:

    Calendar c = Calendar.getInstance();
    c.setLenient(false);
    c.set(Calendar.MONTH, Calendar.FEBRUARY);
    c.set(Calendar.DAY_OF_MONTH, 31);
    System.out.println(c.getTime());

But the exception is only thrown when c.getTime() is called. Calls to getActualMaximum() still work and return 31. So lenient is not useful here.

Lessons learned

The quick fix for this problem is to set the DAY_OF_MONTH to 1 (or any number between 1 and 28) before setting the month.

I also learned that Calendar.setLenient() will not protect you from this error. It will only stop you from getting an invalid Date. It does not protect against overflows.

Sould I use JODA time?

At the end of this post I have a question for you. I have no experience with JODA time always preferring to use the standard Date/Time API unless there was a problem. But perhaps I should reverse my views and use JODA time unless I’m not allowed too? What do you think … is it time to stop  using the default Calendar API and use JODA time instead? Or should I wait for Java 7 with JSR 310?




Temporale data – deel 4: bitemporale collections

Door: gnoij, 7 December 2009

Het is alweer enige tijd geleden dat ik heb geschreven over temporale patterns. In dit deel wil ik het bitemporale pattern uitbreiden met bitemporale collections. Werd met de bitemporale property nog een enkele property historisch gemaakt, in dit deel maken we een lijst historisch. Dat wil zeggen dat er meerdere elementen van een historische lijst tegelijkertijd geldig kunnen zijn.

De klasse BiTemporalObject uit het derde deel blijft hetzelfde. En naast de BiTemporalProperty voor enkelvoudige historische relaties krijgen we nu ook een klasse BiTemporalCollection voor meervoudige historische relaties.

BiTemporalCollection

public class BiTemporalCollection {
 
    protected List alleHistorischeVersies = new ArrayList();
 
    public BiTemporalCollection() {
        super();
    }
 
    public List getActueleVersies() {
        return getVersieOp(new Date(), new Date());
    }
 
    public List getVersieOp(final Date registratieTijdstip, final Date peilDatum) {
        return (List) CollectionUtils.select(this.alleHistorischeVersies, new Predicate() {
            public boolean evaluate(Object object) {
                return ((BiTemporalObject) object).isGeldigOp(registratieTijdstip, peilDatum);
            }
        });
    }
 
    public void voegActueleVersieToe(BiTemporalObject actueleVersie) {
        if (actueleVersie == null) {
            throw new IllegalArgumentException("actueleVersie is null");
        }
        this.alleHistorischeVersies.add(actueleVersie);
    }
 
    public void wijzigActueleVersie(BiTemporalObject nieuweVersie, BiTemporalObject oudeVersie) {
        if (nieuweVersie == null) {
            throw new IllegalArgumentException("nieuweVersie is null");
        }
        if (oudeVersie == null) {
            throw new IllegalArgumentException("oudeVersie is null");
        }
        beeindigVorigeVersie(nieuweVersie, oudeVersie);
        this.alleHistorischeVersies.add(nieuweVersie);
    }
 
    private void beeindigVorigeVersie(final BiTemporalObject nieuweVersie, final BiTemporalObject oudeVersie) {
 
        BiTemporalObject afTeVoerenVersie = zoekActueleVersie(oudeVersie, nieuweVersie.getIngangsdatum(), nieuweVersie.getOpvoerTijdstip()); 
 
        if (afTeVoerenVersie == null) {
            throw new IllegalStateException("Geen oude actuele versie gevonden!");
        }
 
        beeindigVersie(afTeVoerenVersie, nieuweVersie.getIngangsdatum(), nieuweVersie.getOpvoerTijdstip());
    }
 
    public void beeindig(final BiTemporalObject actueleVersie, final Date einddatum) {
        BiTemporalObject afTeVoerenVersie = zoekActueleVersie(actueleVersie, einddatum, new Date());
        if (afTeVoerenVersie == null) {
            throw new IllegalStateException("Geen oude actuele versie gevonden!");
        }
        beeindigVersie(afTeVoerenVersie, einddatum, new Date());
    }
 
    public void beeindig(final Date einddatum) {
        List afTeVoerenVersies = (List) CollectionUtils.select(this.alleHistorischeVersies, new Predicate() {
            public boolean evaluate(Object object) {
                BiTemporalObject versie = (BiTemporalObject) object;
                return versie.isGeldigOp(new Date(), einddatum);
            }
        });
 
        CollectionUtils.forAllDo(afTeVoerenVersies, new Closure() {
            public void execute(Object input) {
                beeindigVersie((BiTemporalObject) input, einddatum, new Date());
            }
        });
    }
 
    private void beeindigVersie(BiTemporalObject versie, Date einddatum, Date registratieTijdstip) {
        BiTemporalObject kopieVersie = versie.kopieer(einddatum, registratieTijdstip);
        this.alleHistorischeVersies.add(kopieVersie);
    }
 
    private BiTemporalObject zoekActueleVersie(final BiTemporalObject actueleVersie, final Date peildatum, final Date registratieTijdstip) {
        return (BiTemporalObject) CollectionUtils.find(this.alleHistorischeVersies, new Predicate() {
            public boolean evaluate(Object object) {
                BiTemporalObject versie = (BiTemporalObject) object;
                return versie.equals(actueleVersie) && versie.isGeldigOp(registratieTijdstip, peildatum);
            }
        });
    }

De wijzigingen ten opzichte van de BiTemporalProperty uit het vorige deel zijn dat er nu niet één maar meer versies tegelijkertijd geldig kunnen zijn (getActueleVersies) en dat we versies kunnen toevoegen (voegActueleVersieToe) en kunnen wijzigen (wijzigActueleVersie). Bij deze laatste methode we de te wijzigen versie meegeven, zodat deze beëindigd kan worden en de nieuwe versie toegevoegd wordt. Ook kunnen we alle versies van de collection beëindigen (beeindig) of een enkele versie (die dan weer aan de methode meegegeven moet worden).

Een voorbeeld

In dit voorbeeld wordt het voorbeeld uit deel 3 uitgebreid met een manager (zelf ook een werknemer), die meerdere werknemers onder zich heeft.

Manager

public class Manager extends Werknemer {
 
    private static final long serialVersionUID = 1L;
 
    private BiTemporalCollection werknemers = new BiTemporalCollection();
 
    public Manager() {
        super();
    }
 
    public Manager(String naam) {
        super(naam);
    }
 
    public void voegWerknemerToe(Werknemer werknemer, Date ingangsdatum) {
        this.werknemers.voegActueleVersieToe(new WerknemerRelatie(werknemer, ingangsdatum));
    }
 
    public List getTeManagenWerknemers() {
        List relaties = getWerknemerRelaties();
        CollectionUtils.transform(relaties, new Transformer() {
            public Object transform(Object object) {
                return ((WerknemerRelatie)object).getWerknemer();
            }
        }) ;
        return relaties;
    }
 
    public List getTeManagenWerknemers(Date registratieTijdstip, Date peildatum) {
        List relaties = getWerknemerRelaties(registratieTijdstip, peildatum);
        CollectionUtils.transform(relaties, new Transformer() {
            public Object transform(Object object) {
                return ((WerknemerRelatie)object).getWerknemer();
            }
        }) ;
        return relaties;
    }
 
    public List getWerknemerRelaties() {
        return this.werknemers.getActueleVersies();
    }
 
    public List getWerknemerRelaties(Date registratieTijdstip, Date peildatum) {
        return this.werknemers.getVersieOp(registratieTijdstip, peildatum);
    }
 
    public void wijzigWerknemer(Werknemer nieuweWerknemer, Werknemer oudeWerknemer, Date wijzigingsdatum) {
        WerknemerRelatie nieuweRelatie = new WerknemerRelatie(nieuweWerknemer, wijzigingsdatum);
        WerknemerRelatie oudeRelatie = bepaalWerknemerRelatie(oudeWerknemer);
        this.werknemers.wijzigActueleVersie(nieuweRelatie, oudeRelatie);
    }
 
    private WerknemerRelatie bepaalWerknemerRelatie(final Werknemer werknemer) {
        return (WerknemerRelatie)CollectionUtils.find(this.werknemers.getActueleVersies(), new Predicate() {
            public boolean evaluate(Object obj) {
                return ((WerknemerRelatie)obj).getWerknemer().equals(werknemer);
            }
        });
    }
 
    public void stopManagenVanWerknemer(Werknemer werknemer, Date einddatum) {
        WerknemerRelatie relatie = bepaalWerknemerRelatie(werknemer);
        this.werknemers.beeindig(relatie, einddatum);
    }
 
    public void stopManagen(Date einddatum) {
        this.werknemers.beeindig(einddatum);
    }
}

Deze klasse representeert de manager. Als een manager een werknemer gaat managen, wordt de werknemer toegevoegd aan de lijst van te managen werknemers (voegWerknemerToe). Als een werknemer wordt vervangen door een andere werknemer wordt de methode wijzigWerknemer aangeroepen, waardoor de relatie met de oude werknemer wordt beëindigd en de nieuwe werknemer wordt begonnen. Als een werknemer uit dienst gaat of naar een andere afdeling, stopt de manager met het managen van deze werknemer (stopManagenVanWerknemer), waardoor de historische relatie van de manager met de werknemer wordt beëindigd. Als de manager stopt met managen (stopManagen), wordt de relatie met alle gemanagede werknemers beëindigd.

De historische relatie van de manager met de werknemer wordt gerepresenteerd door de klasse WerknemerRelatie.

WerknemerRelatie

public class WerknemerRelatie extends BiTemporalObject {
 
    private static final long serialVersionUID = 1L;
 
    private Werknemer werknemer;
 
    protected WerknemerRelatie() {
        super();
    }
 
    protected WerknemerRelatie(Werknemer werknemer, Date ingangsdatum) {
        super(ingangsdatum);
 
        this.werknemer= werknemer;
    }
 
    public Werknemer getWerknemer() {
        return this.werknemer ;
    }
}

De test ziet er als volgt uit:

public void testManager() {
 
	Manager jan = new Manager("Jan");
 
	// simuleer de opvoer tijd 1-1-2003
	Date aanvangsdatum = DateUtils.maakDate(2003, 1, 1);
	BiTemporalObject.TEST_OPVOER_TIJDSTIP = aanvangsdatum; 
 
	Afdeling inkoop = new Afdeling("Inkoop");
	jan.setAfdeling(inkoop, aanvangsdatum);
 
	Werknemer kees = new Werknemer("Kees");
	kees.setAfdeling(inkoop, aanvangsdatum);
	jan.voegWerknemerToe(kees, aanvangsdatum);
 
	Werknemer piet = new Werknemer("Piet");
	piet.setAfdeling(inkoop, aanvangsdatum);
	jan.voegWerknemerToe(piet, aanvangsdatum);
 
	assertEquals(2, jan.getTeManagenWerknemers().size());
	assertTrue(jan.getTeManagenWerknemers().contains(kees));
	assertTrue(jan.getTeManagenWerknemers().contains(piet));
 
	Date wijzigingsdatum = DateUtils.maakDate(2006, 1, 1);
	BiTemporalObject.TEST_OPVOER_TIJDSTIP = wijzigingsdatum; 
 
	Werknemer johan = new Werknemer("Johan");
	johan.setAfdeling(inkoop, wijzigingsdatum);
 
	// Johan vervangt Piet, die uit dienst gaat
	jan.wijzigWerknemer(johan, piet, wijzigingsdatum);
	piet.uitDienst(wijzigingsdatum);
 
	assertEquals(2, jan.getTeManagenWerknemers().size());
	assertTrue(jan.getTeManagenWerknemers().contains(johan));
	assertFalse(jan.getTeManagenWerknemers().contains(piet));
 
	// jan managete gisteren piet nog wel
	Date gisteren = DateUtils.maakDate(2005, 12, 31);
	assertTrue(jan.getTeManagenWerknemers(new Date(), gisteren).contains(piet));
 
	Date einddatum = DateUtils.maakDate(2009, 1, 1);
	BiTemporalObject.TEST_OPVOER_TIJDSTIP = wijzigingsdatum; 
 
	jan.stopManagenVanWerknemer(kees, einddatum);
	kees.uitDienst(einddatum);
 
	assertEquals(1, jan.getTeManagenWerknemers().size());
	assertFalse(jan.getTeManagenWerknemers().contains(kees));
 
	jan.stopManagen(einddatum);
	assertTrue(jan.getTeManagenWerknemers().isEmpty());
}

In deze test maken we een manager Jan aan, die werkt op de afdeling Inkoop op 1 januari 2003. Vanaf deze dag begint hij ook met het managen van twee werknemers Kees en Piet. Vanaf 1 januari 2006 vervangt een nieuwe werknemer Johan Piet, die uit dienst gaat. Op 1 januari 2009 stopt Jan met het managen van al zijn werknemers.

Hibernate mappings

De hibernate mapping van de klasse WerknemerRelatie is analoog aan de mapping van de AfdelingRelatie uit deel 3.

De mapping van de werknemer is uitgebreid met de subclass Manager, die de werknemers als BiTemporalCollection property mapt.

Werknemer.hbm.xml

<hibernate-mapping package="nl.ordina.bitemporal.example" default-access="field">
	<class name="Werknemer" table="Werknemer" discriminator-value="Werknemer">
		<id name="id" column="id">
			<generator class="identity"></generator>
		</id>
		<discriminator column="type" />
		<version name="versie" />
 
		<!-- properties -->
		<property name="naam" column="naam" />
 
		<!-- de verwijzing naar de afdelingrelatie -->
		<component name="afdelingRelatie" 
			class="nl.ordina.temporal.bitemporal.BiTemporalProperty"
			lazy="false">
			<bag name="alleHistorischeVersies"
				cascade="all, delete-orphan" >
				<key column="werknemerId" />
				<one-to-many
					class="AfdelingRelatie" />
			</bag>
		</component>
 
		<subclass name="Manager" discriminator-value="Manager">
		<!-- de verwijzing naar de werknemerrelatie -->
			<component name="werknemers" 
				class="nl.ordina.temporal.bitemporal.BiTemporalCollection"
				lazy="false">
				<bag name="alleHistorischeVersies"
					cascade="all, delete-orphan" >
					<key column="managerId" />
					<one-to-many
						class="WerknemerRelatie" />
				</bag>
			</component>
		</subclass>
	</class>
</hibernate-mapping>

LET OP: In bovenstaand artikel wordt gesproken over BiTemporalCollection. Dit is niet dezelfde als de BiTemporalCollection van Fowler , wat eigenlijk de BiTemporalProperty uit deel 3 is.




What is OSGi? (short version)

Door: Stephan Oudmaijer, 29 May 2009

Since modularisation is a hot topic for Java 7, most developers by now should have a basic understanding about what OSGi is. But I often get the question; What is OSGi?

OSGi is The Dynamic Module System for Java[TM]. OSGi enables Java applications to become modulair and express dependencies on (versions of) other modules. Which means that your application no longer depend on the classloader (not) taking care of dependencies. There are a number of issues OSGi addresses, but I wont even bother writing them down here since Peter Kriens has an excellent blog post about those issues.

Java 7, and in particular JSR294, also tries to solve the modularisation problem. The expert group is working with the OSGi alliance to implement modularisation in Java, but they still have a long way to go. If you are interested in the progress of the expert group you can follow the JSR294 EG mailing list.

Below is a list of resources about OSGi:

  • http://www.infoq.com/presentations/osgi-the-foundation
  • http://www.infoq.com/presentations/colyer-server-side-osgi
  • http://www.infoq.com/presentations/dm-Server-Rod-Johnson
  • http://www.osgi.org/blog/2008_12_01_archive.html
  • http://www.theserverside.com/tt/articles/article.tss?l=MigratingPathToOSGi

Now you know!




Compare with generics please..!

Door: Roy van Rijn, 18 May 2009

I’ve been developing with Java 5+ for quite a while now. Not all developers are this lucky, some are still stuck with 1.4… some even with 1.3! But my clients all made the excellent step forward to Java 5 (some even to 6). The problem is, they moved the runtime/JDK but most clients forget to move their developers!

The language brings some good improvements, the for-loop is easy to understand, and almost all the developers are using this by now. The problem starts with generics. There is a part most developers understand, the Collections API. Almost all programmers use lists now as: List<Integer> instead of a plain old List. This is a good start, but it must not end here! First, I must admit, generics in Java can sometimes be hard and confusing (when using <? extends X> and <? super X>). So I’m not talking about the hard stuff. Its the use of ‘easy’ generics that can be extended to make life so much easier.

For example the piece of code below:

public class LabelPlaceholderComparator implements Comparator { 
 
    /**{@inheritDoc */ 
    public int compare(Object o1, Object o2) { 
        LabelPlaceholder p1 = (LabelPlaceholder) o1; 
        LabelPlaceholder p2 = (LabelPlaceholder) o2; 
        return p1.getLabel().compareTo(p2.getLabel()); 
    }
}

Of course, there seems to be not much wrong with the code, I see it all the time. Yes, the code breaks if you put something else in the comparator, but hey… the Javadoc says it only accepts LabelPlaceholders! So lets use this code:

List<LabelPlaceholder> holders = fillList();
Collections.sort(holders, new LabelPlaceholderComparator());

Done! Its working and no problems right? Wrong. The IDE complains about this code. For example Eclipse shouts:
 
Type safety: The expression of type LabelPlaceholderComparator needs unchecked conversion to conform to Comparator<? super T>

At this point, most programmers at the company I work for now will just ignore this warning. They might even add:

@SuppressWarnings("unchecked")

What a shame… First of all, what is Eclipse trying to tell us here? The compiler doesn’t know we created the Comparator with only LabelPlaceholders in mind. But the compiler does know (with generics) that the List only contains LabelPlaceholders. So the warning is (in understandable English):

I’ve got a list here of T (LabelPlaceholders) and a Comparator for Objects, this can go wrong! I’d rather have a specific Comparator for this job. Do you have one for me?

The solution to this problem is very simple, but most neglect to use it:

public class LabelPlaceholderComparator implements Comparator<LabelPlaceholder> { 
 
    /**{@inheritDoc */ 
    public int compare(LabelPlaceholder p1, LabelPlaceholder p2) { 
        return p1.getLabel().compareTo(p2.getLabel()); 
    }
}

As you can see, the code is much smaller. The interface is now generified, it knows we are going to compare LabelPlaceholders now, nothing more, nothing less. Also, we don’t have to cast anymore, because of the generics you can’t put anything else in there.

So, lets go to the conclusion: Why is the latter code better code?

  1. As you can see, the code is smaller!
  2. There are no casts, the code is safer (no ClassCastException or eleborate class checks)
  3. If somebody uses your code, he/she knows what kind of objects the Comparator can handle. You don’t have to read the Javadoc or the code to see what it does.

Throughout the projects I encounter I keep finding examples of places where generics would have made the code smaller/safer/more understandable. For some reason the programmers still only use generics on collections. So, even though generics aren’t perfect, please use them where/when you can, it’ll always add clarity to the code, and most of the time it’ll also make your code safer, and in some cases the code gets smaller because you can leave away casts and class-checks.

Don’t ever let me see public int compare(Object o1, Object o2); again! :-)




Boekbespreking: Programming in Scala

Door: Hedzer Westra, 12 April 2009

Programming in Scala

Auteurs: Odersky, Spoon & Venners, publicatiejaar: 2008 (1e druk november, 2e druk binnenkort), uitgever: Artima, pagina’s: 736, ISBN-13: 978-0-9815316-0-1, website: http://www.artima.com/shop/programming_in_scala, prijzen: Jolt Productivity Award 2009 for Technical Books.

Belofte maakt schuld: het is tijd voor mijn eerder aangekondigde boekbespreking van “Programming in Scala”.

Ik vond het een prettig & helder boek. In 33 goed leesbare en goed geordende hoofdstukken worden (bijna) alle onderdelen van de taal en de bijbehorende bibliotheek beschreven.

De hoofdstukken zijn goed gebalanceerd – niet te lang en niet te kort, niet te simpel en niet te ingewikkeld. “Programming in Scala” is helder van structuur, bevat een uitgebreide woordenlijst en slechts een korte bibliografie.

De auteurs hebben het niet gedaan, maar je kunt wat mij betreft de hoofdstukken ruwweg in 6 delen onderverdelen: inleiding, basis, standaardelementen, gevorderd, extra’s en frameworks.

De tekst bevat veel vooruitverwijzingen en voetnoten. Het onderwerp van veel hoofdstukken wordt een aantal hoofdstukken later weer opgepakt, zodat dat onderwerp even wat tijd heeft gehad om te bezinken. De ‘fast tracks’ maken het mogelijk om lastiger of verdiepende stukken over te slaan.

Naast uitleg over wat Scala is, wordt ook veel duidelijk gemaakt over wat je er mee kunt doen; het verschil in programmeerstijl tussen Imperatief Programmeren (IP) en Functioneel Programmeren (FP) wordt diverse keren behandeld – er is veel voorbeeldcode die laat zien hoe je recursief, en in ’t algemeen in FP-stijl, kunt programmeren. Ook conventies die in de community ontwikkeld zijn, komen aan bod.

Het boek is tevens beschikbaar als eBook – ik had vanaf afgelopen september een preprint tot mijn beschikking. Ik was verbaasd over de leesbaarheid ervan – zelfs op een gewone laptop; niets eens op een speciale eBook reader. Ook is alle voorbeeldcode te downloaden.

Er wordt vrij weinig gerefereerd naar andere talen dan Java. Sowieso vind ik het boek goed leesbaar voor Javanen, maar of dit ook geldt als je kennis meer ligt bij andere talen, vraag ik me af.

Als tipje van de sluier wat betreft de inhoud volgt hier een (korte?) opsomming van de taalaspecten die behandeld worden: traits, closures, case classes, type parameters inclusief variance & erasure, implicits, pattern matching & extractors, for expressions, operator overloading, interne & externe DSL’s, lazy evaluation, currying, 1ste-klasse & hogere-orde functies, typehiërarchie en bibliotheken (onder anderen collecties, XML, Actors, parser combinator, Swing, testen).

Hoofdstuk 28 vond ik een vreemde: hierin wordt namelijk stap voor stap uitgelegd hoe je netjes equals() & hashCode() kunt implementeren. Inderdaad belangrijk, maar helemaal niet Scala-specifiek. Er wordt dan ook veelvuldig wordt gerefereerd naar Joshua Bloch’s “Effective Java.”

Het hoofdstuk over integratie tussen Scala & Java viel me wat tegen – het is erg kort. Nu is het wel zo dat Scala & Java naadloos integreren, en er dus niet al te veel aandacht aan besteed hoeft te worden, maar iets meer had geen kwaad gekund.

Het afsluitende voorbeeld vond ik wel erg interessant. In minder dan 200 regels code worden erg veel taalaspecten gebruikt, en de volgende technieken worden gecombineerd tot een spreadsheetapplicatie: Interne & externe DSL (Swing & formules), termevaluatie en separation of concerns – MVC en Pub/Sub (op 2 niveaus).

“Programming in Scala” is niet bedoeld als referentie of handleiding; dit is duidelijk een leerboek, onder anderen vanwege de grotere & kleinere codevoorbeelden, die de tekst extra verhelderen. Mijn aanbeveling is om ‘t van voor tot achter te lezen, en niet hapsnap, anders zul je details missen. Al met al biedt het boek op een toegankelijke manier een complete behandeling van de taalaspecten van Scala, en kun je – tijdens het lezen al – zelf aan de gang met deze erg interessante taal.

Kleurindicatie: Groen (3 op schaal van 3)

Nu ik toch jullie aandacht heb wat betreft Scala: op dinsdag 9 juni geef ik een TechSessie over Scala. Primair bedoeld voor J-Tech’ers, maar natuurlijk zijn alle Ordina-medewerkers welkom. Binnenkort kun je een uitnodiging in de mail verwachten!

Hedzer Westra

Hedzer Westra




JDK 7 language changes: DeVoxx poll

Door: Jan-Kees van Andel, 8 March 2009

Afgelopen DeVoxx is een stemming geweest waar de bezoekers konden stemmen op hun favoriete nieuwe JDK 7 features.
De grote wijzigingen zijn natuurlijk allang bepaald, zoals !closures (niet dus), JVM optimalisaties voor scripttalen (JSR 292) en Java Module System (JSR 294), maar voor JDK 7 willen ze ook een aantal kleinere wijzigingen faciliteren.
Stephen Colebourne heeft op zijn blog een mooie samenvatting van de poll geplaatst.
Zoals je kunt zien, zitten er 3 populaire tussen:

  1. Null handling
  2. Infer generics
  3. Multi catch

Properties is ook populair, maar deze komt sowieso niet in JDK 7.
Deze 3 populaire features zal ik even kort laten zien:

Null handling
Iedereen heeft weleens dergelijke code gezien:

if (myObject != null) {
    if (myObject.getProp() != null) {
        if (myObject.getProp().getProp() != null) {
            return myObject.getProp().getProp().getString();
        }
    }
}

Zou het niet relaxed zijn als je het zo kunt schrijven?

return myObject()?.getProp()?.getProp()?.getString();

Er zitten nog wat vergelijkbare constructies aan vast, zoals een aanpassing aan de ternary operator, zie Alex Millers blog.

Infer generics
Waarschijnlijk hebben jullie deze constructie ook weleens gezien? Zo nee, dan heb je waarschijnlijk nog geen JDK 5+ collections gebruikt. :)

List<String> list = new ArrayList<String>();

Dit is nog niet zo vervelend, maar wat dacht je van deze variant?

Map<String, AtomicReference<SoftReference<SomeObject>>> list = new ConcurrentHashMap<String, AtomicReference<SoftReference<SomeObject>>>();

Vrij lelijk als je het aan mij vraagt. En in de meeste gevallen is het niet eens nuttig, aangezien links en rechts van de assignment hetzelfde staat.
Wat dacht je hiervan?

Map<AtomicReference<SoftReference<SomeObject>>> list = new ConcurrentHashMap<>();

De haken aan de rechterkant geven aan dat de compiler de typespecificatie links van de assignment naar de rechterkant gekopieerd moet worden.
Zie wederom Alex Millers blog voor de details.

Ten slotte de Multi catch
Jullie hebben ook vast weleens dergelijke catch clausules gehad neem ik aan.

try {
    // Do something
} catch (Exception1 e) {
    LOG.error("Something horrible happened", e);
} catch (Exception2 e) {
    LOG.error("Something horrible happened", e);
} catch (Exception3 e) {
    LOG.error("Something horrible happened", e);
} catch (Exception4 e) {
    LOG.error("Something horrible happened", e);
}

“catch (Exception)” is niet netjes, hebben we allemaal geleerd. Voor je het weet, vang je onterecht een fout af. Maar al die code duplicatie zit je toch niet lekker. Vandaar een nieuw alternatief:

try {
    // Do something
} catch (Exception1 | Exception2 | Exception3 | Exception4 e) {
    LOG.error("Something horrible happened", e);
}

Dat is een stuk beter he? Vooral in combinatie met Safe Rethrow kan dit erg krachtig zijn.
Ik ben nog wel benieuwd naar de details, want wat is het type van “e”? Gaan ze op zoek naar een common supertype, of wordt het duck typing?
En weer een linkje naar Alex Miller.

Dusss…
Dit zijn maar een paar van de toevoegingen waar momenteel aan gewerkt wordt. Meer info over “Project Coin” op Joe Darcy’s blog.
En natuurlijk weer Alex Miller.




Scala: too hot to handle?

Door: Hedzer Westra, 29 January 2009

Het blog item van 21 januari sloot ik af met: “Ikzelf hoop binnenkort te komen met een bespreking van ‘Programming in Scala,’ het eerste boek over deze nieuwe en spannende programmeertaal.”

Prompt reageerde Jan-Kees van Andel: “Ik verwacht niet dat Scala verder zal groeien qua populariteit. Op de JVM Language Summit 2008 was de algemene opinie dat Scala het niet gaat worden omdat het te moeilijk is. En dat ‘te moeilijk’ komt uit de mond van hele capabele mensen (lees: JVM taalontwerpers). Zij waren vooral gecharmeerd van Clojure.”

Aangezien ik me ten doel heb gesteld om Scala dit jaar onder de aandacht te brengen bij mijn J-Tech collega’s, kan ik dat natuurlijk niet over mijn kant laten gaan :-)

Als je het hebt over JVM taalontwerpers: de auteur van Scala, Martin Odersky, is bedenker van Java generics, en ontwikkelaar van de Java 1.2 reference compiler. Zelf maakt hij dus ook deel uit van deze groep, en hijzelf is nog allerminst klaar met Scala.

Onlangs is bekend geworden dat Java7 geen closures zal krijgen – men kon geen beslissing nemen. Eind augustus/begin september postte Jan-Kees nog hoopvol een artikel waarin de drie onderhanden voorstellen behandeld werden, maar helaas zal geen ervan het licht gaan zien in Java7. Dat op zich is al een goed argument om naar Scala te kijken: daar zitten closures rotsvast in de basis van de taal.

Natuurlijk heeft Clojure ook closures (vandaar de naam…), maar als je kijkt naar de syntax van Clojure, zie je meteen waar deze voornamelijk op gebaseerd is: LISP. En dat staat niet voor niets voor Lots of Irritating Silly Parentheses (eigenlijk LISt Processing, maar je snapt waar ik heen wil). LISP stamt uit de jaren ‘50/’60 van het vorige millennium en heeft begrijpelijkerwijs een inmiddels verouderde syntax. De Scala syntax daarentegen is grotendeels op Java gebaseerd, wat de overstap Java -> Scala mijns inziens eenvoudiger maakt.

Een ander punt is dat ik een beetje gelogen heb toen ik zei “deze nieuwe [..] programmeertaal”: Scala is al sinds 2001 in ontwikkeling, en niet door de minste wetenschappers, en is zodoende al vergaand uitontwikkeld. Clojure begon pas in 2005.

Scala’s propositie is dat het een schaalbare taal is (‘SCAlable LAnguage’). Niet alleen in de zin van performance bij gebruik op multi-core systemen, maar ook – en veel belangrijker – schaalbaar naar het type applicatie dat je er mee wilt bouwen. Dat varieert van script via desktop applicatie tot enterprise systeem. De syntax ondersteunt al deze applicaties op dezelfde manier. Dat gezegd hebbende, is Scala een rijke taal, die je – naar wens – kunt gebruiken als ‘Java on steroids’, dan wel als een bijna-pure functionele taal à la Haskell. Als je dat laatste wilt, en momenteel alleen Java beheerst, is dat inderdaad nogal een klus, en kan ik me voorstellen dat je de taal als ‘te moeilijk’ bestempelt. Maar als je kiest voor het eerste, en je je de taal langzamerhand eigen maakt, kunt je zogezegd ‘opschalen’ naar een meer functioneel gerichte programmeerstijl. Kan allemaal in Scala.

Wat betreft de functionele programmeerstijl (FP): die is echt fundamenteel anders dan de imperatieve OO programmeerstijl die voor bijvoorbeeld Java de voorkeur geniet. Dat moet je dus leren, en leren is altijd (in meer of mindere mate) moeilijk – maar om daar meteen de taal de schuld van te geven? Als je overstapt van C naar Java moet je ook een nieuwe, in het begin moeilijke, stijl aanleren, maar uiteindelijk is dat de moeite dubbel en dwars waard. Voor FP geldt dat evenzogoed. Als je echt ‘hardcore’ puur FP wilt proeven, zou je eens kunnen kijken naar Haskell of ML. Maar dat is niet voor niets, ondanks jaren taalontwikkeling, nog steeds het domein van wiskundig opgeleide academici – programmeren in die talen wordt snel heel erg abstract (hoor ik iemand ‘Monads!’ roepen?). Scala is veel meer ‘down to earth’ en bereikbaar voor de gewone stervelingen.

Mocht je nog niet overtuigd zijn dat Scala toch echt wel wat meer aandacht verdient, check dan de Wiki die ik ingericht heb (helaas alleen voor Ordinamedewerkers). Daarop vind je een powerpoint van een presentatie die ik onlangs gaf bij een architectuur intervisie meeting, een draft cookbook en een Eclipse workspace met veel kleine codevoorbeeldjes. Wel eerst even de Scala plugin installeren!

Een special meeting Scala zit in de koker. Ook kun je extra blog items van me verwachten over bijvoorbeeld Liftweb en Sweet, twee web frameworks in Scala. A propos: ik ben nog op zoek naar reviewers voor mijn kookboek! Wie meldt zich aan als proeflezer?

Hedzer Westra

Hedzer Westra




[Closures] Introductie

Door: Jan-Kees van Andel, 29 August 2008

Closures, één van de meest bediscussieerde onderwerpen van de afgelopen paar jaren in de Java wereld. Deels een hype, deels bieden closures meerwaarde voor Java development. Ik ga niet meedoen met alle discussies waarom ze (niet) gewenst zijn, maar het is een feit dat het mogelijkheden biedt qua API design. In dit stuk hoop ik a) de mystieken van closures iets duidelijker te maken en b) wat inzicht te geven in de huidige closures oorlog bij Java.

Closures

Wat is een closure? WikiPedia (http://en.wikipedia.org/wiki/Closure_(computer_science)) heeft de volgende nietszeggende definitie: “In computer science, a closure is a function that is evaluated in an environment containing one or more bound variables. When called, the function can access these variables.”. Hoewel deze definitie klopt, is hij niet echt duidelijk. Waar komt het op neer?

Bij een closure wordt een functie uitgevoerd in een andere context dan waarin deze wordt gecreëerd. Deze zogenaamde “function context” wordt bij het creëeren van de functie bij de referentie naar de functie opgeslagen. Dit betekent dat als iemand deze referentie te pakken krijgt en de functie aanroept, de variabelen uit de context waarin de functie gemaakt is, beschikbaar zijn.

Voordat ik concreter wordt, wil ik nog even aangevan dat closures niet nieuw zijn. In vrijwel elke mainstream taal zit een soortgelijke constructie. Ook in Java, namelijk met anonymous inner classes, maar andere talen, zoals Smalltalk, JavaScript en Ruby bieden veel meer flexibiliteit.

Nog steeds nietszeggend? Dan maar een codevoorbeeld (JavaScript):

<html>
  <head>
    <script type="text/javascript">
      function initPage() {
        var counter = 0;
        window.onclick = function() {
          alert("Click #" + ++counter);
        };
      }
      window.onload = initPage;
    </script>
  </head>
  <body></body>
</html>

Wat gebeurt hier? Om te beginnen zijn functies in JavaScript first class objecten, in tegenstelling tot in Java, waarin methoden altijd onderdeel van een object zijn. Dit betekent onder andere dat je naar een functie kunt refereren, of deze referentie in een variabele kunt stoppen om deze daarna rond te gooien. Dit kun je zien aan de regel “window.onclick = …” waarin een object van het type function aan de onclick property van het window object toegekend wordt.

Wat (hopelijk) ook opvalt is dat er een function in een andere function gedeclareerd wordt. In dit voorbeeld spreken we van een anonymous inner function. Dit is een belangrijk detail, aangezien dit voor de JavaScript interpreter betekent dat er een closure gecreeerd moet worden. Wat betekent dit?

Kort gezegd betekent een closure dat bij de referentie naar de anonymous inner function, alle variabelen uit de enclosing scope bewaard blijven. Vandaar de naam closure. Deze variabelen vormen de function context welke vanaf nu een deel van de functie geworden. Elke keer als je deze functie aanroept, zijn de variabelen in de function context beschikbaar.

Wat is het nut dan van deze closure? Nou, de variabele “counter” heeft een function scope en verdwijnt op het moment dat de functie eindigt. Bij een event handler zoals in bovenstaand voorbeeld betekent dit dus dat de variabele op het moment van de klik niet bruikbaar is, aangezien het click-event niet in dezelfde stack plaatsvindt. Dit betekent dus dat je zonder closures een globale variabele nodig hebt om een simpel tellertje te maken. Met closures heb je deze beperking niet, want de variabele counter blijft bewaard en de ++ operator werkt de variabele in de function context bij, zodat het ook een echte teller is.

Conclusie

Closures bieden dus de mogelijkheid om stack confined variabelen te bewaren over meerdere stacks heen en zijn daardoor dus zeer geschikt voor event handlers. Dat is ook de reden dat ze in JavaScript veel gebruikt worden. JavaScript wordt voornamelijk gebruikt voor het koppelen van event handlers aan buttons etc. Daarnaast biedt het een mooie compacte syntax.

Volgende keer kijken we naar de Java closures initiatieven.