Fluent Java library for collections: op4j

29. May, 2010

op4j is a nice little library for all those of us who need to work with collections. Here is a little example:

  Calendar now = Calendar.getInstance();
  Set<Calendar> set = 
      Op.on(list)
      .toSet()
      .map(FnString.toCalendar("dd/MM/yyyy"))
      .removeAllNullOrTrue(FnCalendar.after(now))
      .get();

That takes a list of strings, converts them to Calendar instances and removes all dates which are null or in the future. Questions?


Type-safe object map

28. May, 2010

Wouldn’t it be nice if you could do this:

        TypedMap map = new TypedMap();
        
        String expected = "Hallo";
        map.set( KEY1, expected );
        String value = map.get( KEY1 ); // Look Ma, no cast!
        assertEquals( expected, value );
        
        List<String> list = new ArrayList<String> ();
        map.set( KEY2, list );
        List<String> valueList = map.get( KEY2 ); // Even with generics
        assertEquals( list, valueList );

Note: The type checking is at compile time. No runtime cost!

As you can see, I get different types from the map without casting. How is that possible? Well, Generics can be your friends. The magic is in the key:

    final static TypedMapKey<String> KEY1 = new TypedMapKey<String>( "key1" );
    final static TypedMapKey<List<String>> KEY2 = new TypedMapKey<List<String>>( "key2" );

The keys contains two pieces of information: The actual key (a string) and the type which the value of the key has. The class for the key is completely braindead:

public class TypedMapKey<T> {
    private String name;
    
    public TypedMapKey(String name) {
        this.name = name;
    }
    
    public String name() {
        return name;
    }
}

The trick is to use the auto-resolution of the type at compile time in TypedMap. As you can see below, I need to suppress warnings about a typecast but the nice thing is: I only have to do it in this central place and the annotation is always correct (well, until you start to use set(String)).

As you can also see, I use delegation. I could have extended map but I wanted to show a pattern which you can use in other places … like HttpRequest or HttpSession.

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class TypedMap implements Map<String, Object> {
    private Map<String, Object> delegate;
    
    public TypedMap( Map<String, Object> delegate ) {
        this.delegate = delegate;
    }

    public TypedMap() {
        this.delegate = new HashMap<String, Object>();
    }
    
    @SuppressWarnings( "unchecked" )
    public <T> T get( TypedMapKey<T> key ) {
        return (T) delegate.get( key.name() );
    }
    
    @SuppressWarnings( "unchecked" )
    public <T> T remove( TypedMapKey<T> key ) {
        return (T) delegate.remove( key.name() );
    }
    
    public <T> void put( TypedMapKey<T> key, T value ) {
        delegate.put( key.name(), value );
    }
    
    // --- Only calls to delegates below
    
    public void clear() {
        delegate.clear();
    }

    public boolean containsKey( Object key ) {
        return delegate.containsKey( key );
    }

    public boolean containsValue( Object value ) {
        return delegate.containsValue( value );
    }

    public Set<java.util.Map.Entry<String, Object>> entrySet() {
        return delegate.entrySet();
    }

    public boolean equals( Object o ) {
        return delegate.equals( o );
    }

    public Object get( Object key ) {
        return delegate.get( key );
    }

    public int hashCode() {
        return delegate.hashCode();
    }

    public boolean isEmpty() {
        return delegate.isEmpty();
    }

    public Set<String> keySet() {
        return delegate.keySet();
    }

    public Object put( String key, Object value ) {
        return delegate.put( key, value );
    }

    public void putAll( Map<? extends String, ? extends Object> m ) {
        delegate.putAll( m );
    }

    public Object remove( Object key ) {
        return delegate.remove( key );
    }

    public int size() {
        return delegate.size();
    }

    public Collection<Object> values() {
        return delegate.values();
    }
}

Update 28. June 2010: As noticed by a couple of people on StackOverflow, this isn’t type safe if you define two keys with the same name.

Note that the code above is to wrap an existing map with a more type-safe API. If you want to make this even more type safe, create the map like so:

import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

public class TypedMap implements Map<TypedMapKey<?>, Object> {
    private Map<TypedMapKey<?>, Object> delegate;
    
    public TypedMap( Map<TypedMapKey<?>, Object> delegate ) {
        this.delegate = delegate;
    }

    public TypedMap() {
        this.delegate = new HashMap<TypedMapKey<?>, Object>();
    }
    
    @SuppressWarnings( "unchecked" )
    public <T> T get( TypedMapKey<T> key ) {
        return (T) delegate.get( key.name() );
    }
...
}

Haul 1.20 Waiting Torture

26. May, 2010

Finally I managed to post the next scene. Sorry again for the delay but RL took its toll. Don’t forget to post a comment if you like the story (and you must post a comment if you don’t like it!)

1.20 Waiting Torture – To his surprise, Forne Rako finds himself alive. He’s not so sure how happy he should feel about it.

Table of contents

Previous post


Endlich habe ich es geschafft die nächste Szene hochzuladen. Vergiss nicht einen Kommentar zu posten, wenn dir die Geschichte gefällt (und wenn sie dir nicht gefällt, dann musst du einen Kommentar posten!)

1.20 Wartefolter – Zu seiner Überraschung stellt Forne Rako fest, dass er noch lebt. Er ist aber nicht sicher, wie glücklich er darüber sein sollte.

Inhaltsverzeichnis


Next scene of Haul on hold

24. May, 2010

Haven’t been able to post the next two scenes of Haul because of RL. Working on it.


Gall’s Law

21. May, 2010

I just stumbled over Gall’s Law:

“A complex system that works is invariably found to have evolved from a simple system that worked. The inverse proposition also appears to be true: A complex system designed from scratch never works and cannot be made to work. You have to start over, beginning with a working simple system.”

I you ever join a software project and someone mentions “complete rewrite”, you know two things for a fact:

  1. The current software sucks
  2. The rewrite will suck more.

Not convinced? See what Joel Spolsky has to say: Things You Should NEVER Do, Part I

If you still won’t believe, come back after the complete rewrite.


hg convert and “abort: Interrupted system call”

20. May, 2010

If you get “abort: Interrupted system call” when running “hg convert”, then see this issue for a workaround.


Worse-is-better

18. May, 2010

I just stumbled over “The Rise of “Worse is Better”“. The article deals with the “get it right the first time” and the “get it as right as possible” dilemma. In Software development, you often have a situation where you don’t know enough for “get it right 100%” and you don’t have the time to learn. Or “get it right 100%” just isn’t possible.

In the end, “do it as good as you can” is, all things considered, better than the alternative. Or as Bill Gates allegedly said: “Windows doesn’t contain any bugs which any big number of users wants to have fixed.”

Which explains nicely why programming languages which strive for perfectionism (like Lisp) never really caught on. There are just too few perfectionists – and it’s a recessive trait.