After thinking a while about Linq for Java I started to experiment. My main driver was to find out how Linq would look like in Java. I started with a simple task: From a given range of floats convert ever item to an integer and take those that are less or equal four. In C# this would look like this:
IEnumerable<float> source =
new []{0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
IEnumerable<int> result =
source.Select(x=>(int)x).Where(x=> x<= 4);
Java has neither delegates nor any lambda syntax, so statements like x=>x<=4 are not possible. So I decided to model the delegates as interfaces:
public interface UnaryFunction<Arg, Result> {
Result execute(Arg arg);
}
Linq statements basically are so called extension methods to the IEnumerable<T> and/or IQueryable<T> interface. The Java equivalent is the Iterable<T> interface. However, there is nothing like an extension method in java, so I had to fall back on the OO way of extension: inheritance.
public interface Queryable<T> extends Iterable<T>{
<U> Queryable<U> select(UnaryFunction<T, U> select);
Queryable<T> where(UnaryFunction<T, Boolean> pred);
}
So if every operation returns a new Queryable<T> object, it would be possible to chain the calls very similar to the C# way.
This looked very promising! I decided that the real work should be done on the Iterator<T> level and implemented specific iterators for the select and where operations. Here is the SelectIterator as an example:
class SelectIterator<From, To> implements Iterator<To> {
private final Iterator<From> inner;
private final UnaryFunction<From, To> select;
SelectIterator(Iterator<From> inner,
UnaryFunction<From, To> sel) {
this.inner = inner;
this.select = sel;
}
public boolean hasNext() {
return inner.hasNext();
}
public To next() {
return select.execute(inner.next());
}
public void remove() {
throw new UnsupportedOperationException("");
}
}
I implemented the Queryable<T> interface with an abstract base class and concrete anonymous inner classes:
abstract class QueryableImpl<T> implements Queryable<T> {
<U> Queryable<U> select(final UnaryFunction<T, U> s){
return new QueryableImpl<U>(){
@Override
public Iterator<U> iterator() {
return new SelectIterator(
QueryableImpl.this.iterator(), s
);
}
};
}
// … and some more
public abstract Iterator<T> iterator();
}
Last but not least I provided a static adaptor function to convert any Iterable<T> to a Queryable<T>:
static <T> Queryable<T> from(final Iterable<T> range) {
return new QueryableImpl<T>() {
@Override
public Iterator<T> iterator() {
return range.iterator();
}
};
}
Putting all together allows you to write the following Java code:
Iterable<Float> src=Arrays.asList(0f, 1f, 2f, 3f, 4f, 5f, 6f, 7f, 8f);
UnaryFunction<Float, Integer> xform =
new UnaryFunction<Float, Integer>() {
public Integer execute(Float arg) {
return (int) arg.floatValue();
}
};
UnaryFunction<Integer, Boolean> less5 =
new UnaryFunction<Integer, Boolean>() {
public Boolean execute(Integer arg) {
return arg < 5;
}
};
Iterable<Integer> i2 =
from(src).select(xform).where(less5);
This looks pleasant close to the C# original!
No comments:
Post a Comment