When I find something interesting and new, I post it here - that's mostly programming, of course, not everything.

Thursday, December 25, 2008

Inner Class in Java: Unexpected Volatility

You probably know that if you use a value in an inner class, the evil Java compiler wants you to write "final"; people do all kinds of trick to bypass it - store date in arrays, for instance.

But relax, you do not have to write "final" if it is a member field. Look at this code:

package experimental;

import java.util.AbstractSet;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;

public class VolatileConstant {

public static void main(String[] args) {
Iterable<Set<String>> iss = new Iterable<Set<String>>() {

public Iterator<Set<String>> iterator() {
return new Iterator<Set<String>>() {
int i = 0;
public boolean hasNext() {
return i < 3;
}

public Set<String> next() {
i++;

return new AbstractSet<String>() {
public Iterator<String> iterator() {
return new Iterator<String>() {
int j = 0;
public boolean hasNext() {
return j < i;
}

public String next() {
j++;
return "" + i + "." + j;
}

public void remove() {
throw new UnsupportedOperationException();
}
};
}

public int size() {
return i;
}
};
}

public void remove() {
throw new UnsupportedOperationException();
}
};
}
};

Set<Set<String>> sss = new HashSet<Set<String>>();

for (Set<String> ss : iss) {
sss.add(ss);
System.out.println(ss);
}
System.out.println(sss);
}
}


You know what it prints? It's in the next line, in white color so that you have a choice not to see it.

[1.1]
[2.1, 2.2]
[3.1, 3.2, 3.3]
[[3.1, 3.2, 3.3], [3.1, 3.2, 3.3], [3.1, 3.2, 3.3]]


I am sure you can explain it, after a while, but is not it really amusing?

Saturday, December 13, 2008

Functions

An Excuse

Well, I decided to gather all the code related to Function class in one "universe" container class, Functions. The other option would be to have a separate "package". I do not see any reason for using packages instead of container classes.

My idea regarding container classes is that it is a theory - we have constants, types, rules, axioms, all in one place. An element of theory is meaningless outside of its theory. A set does not make sense without a Set Theory.

So here we have some kind of "Functions Theory". Since functions are defined on types, which vaguely are like classes, which vaguely are like unlimited sets, we can define set-theory-like notions of Injection and Bijection. A Surjection would be nice to have too, but it has no functional influence on current programming practice: nobody knowingly builds factorsets.

Overview

Three abstract classes are defined: 
  • Function - general functionality;
  • Injection - a special kind of Function, not mapping different values to one;
  • Bijection - invertible Function
and several useful extra classes: 
  • Inclusion, - a Function that includes one set into another; 
  • Id - an identity Function
  • PairsToMap - takes a set of pairs, turns it into a Map;
  • IterableToSet - takes an iterable, turns it into a Set.
Also there are several  static methods. Don't be afraid of static methods, they all belong to Functions Theory, and there's hardly a reason to override or polymorph them in any way.

Function

A Function<X, Y> declares an abstract method Y apply(X x), which obviously determines the function's behavior. In addition, Function has methods for mapping an Iterator, an Iterable, a Collection, a List. Note that there's no method that maps a Set. Why? Because if you apply a Function to a Collection, you can have repeating values.

Method toMap(Set<X) converts a Function into a Map; method forMap(Map<X, Y>) turns a Map into a Function. Static compose(f, g) method composes two Functions.

Injection

Injection is a special kind of Function: it maps different arguments to different values. Not that we can verify it; we just trust the declaration. Since a composition of two Injections is an Injection, this case is taken care of. It is. 

An Injection maps a Set to a Set, so this method is there.

Inclusion is a special kind of Injection: it includes one class (set) into its superclass (superset). It maps a value to itself, casting.

Bijection

Bijection<X, Y> is a special kind of Injection: it has an inverse. So, additionally, it declares a method X unapply(Y). A composition of two Bijections is a Bijection


Here is a sample code, where Schwartzian transform is defined:

/**
* Given a function f, builds another function that for each x
* builds Map.Entry(x, f(x))
*
* @param f function to apply
* @return a function that builds pairs.
*/
public static <X, Y> Bijection<X, Pair<X, Y>> schwartzianTransform(final Function<X, Y> f) {
return new Bijection<X, Pair<X, Y>>() {
public Pair<X, Y> apply(final X x) {
return math.cat.LazyPair.LazyPair(x, f);
}

public X unapply(Pair<X, Y> p) {
return p.x();
}
};
}


Oh, and the code is here.

Thursday, December 11, 2008

Java Solutions: inheriting chain calls

Recently chain builder calls in Java have been gaining popularity, look, e.g. at this.

The idea is that you have a builder that has small configuration methods and in the end builds the right stuff. StringBuffer works like this:

 return new StringBuffer().append("Now is ").append(new Date()).append(", and ticking...").toString();


Or, more typically, something like this code:

new TestBuilder().limitTime(1000).forPackage(this.getClass().getPackage()).skipFlaky().build().run();


This way we, first, annotate our parameters, and second, bypass the boring law that demands writing each statement on a separate line.
And of course it makes the process more flexible.

Actually, the fact that it is a builder is unimportant. We can just chain calls if every method that in the previous life was returning void would start returning this.

But there's a problem here... what if our builder is not the ultimate version, but a subclass of a superbuilder? Look at an example below:


public class ChainCalls {
public static class A {
A do1(String s) {
System.out.println("A.1 " + s);
return this;
}

A do2(String s) {
System.out.println("A.2 " + s);
return this;
}
}

public static class B extends A {
B do0(String s) {
System.out.println("B.0 " + s);
return this;
}

B do1(String s) {
System.out.println("B.1 " + s);
return this;
}

B do3(String s) {
System.out.println("B.3 " + s);
return this;
}
}

public static void main(String[] args) {
((B)(new B().do0("zero").do1("one").do2("two"))).do3("three");
}
}


See how we have to cast the result of do2()? That's because the method of class A has no knowledge that it actually works in the context of class B.

How can we deal with this? One cheap solution is to duplicate all the methods in B and call the delegate, that is, super.do1() etc. Feasible, but not very nice, not very scalable, and not very Java5-ish.

Because we have generics, and we have a so-called covariant inheritance, so that - because we can!

An anonymous contributor in livejournal.com has suggested the following solution:

public class ChainCallsCovariant {
public static abstract class SuperA<T> {
abstract T self();

T do1(String s) {
System.out.println("A.1 " + s);
return self();
}

T do2(String s) {
System.out.println("A.2 " + s);
return self();
}
}

public static class A extends SuperA<A> {
A self() { return this; }
}

public static abstract class SuperB<T> extends SuperA<T> {
T do0(String s) {
System.out.println("B.0 " + s);
return self();
}

T do1(String s) {
System.out.println("B.1 " + s);
return self();
}

T do3(String s) {
System.out.println("B.3 " + s);
return self();
}
}

public static class B extends SuperB<B> {
B self() { return this; }
}

public static void main(String[] args) {
new B().do0("zero").do1("one").do2("two").do3("three");
}
}


The main trick here is to encapsulate "return this", and make it generic, so that we always, in the eyes of the compiler, return the instance of the right class.

P.S. Here I've posted a better solution.

Thursday, December 04, 2008

Java Compiler Warnings 2

Say, somewhere in your test code, you need a list of nulls:

List<Direction> directionsWithNullValues = Lists.newArrayList(null, null, null);


Ouch! Compiler warning: unchecked generic array creation of type E[] for varargs parameter

What's going on here? An array can be created, but Java is confused regarding the type. Just cannot deduce it.

The solution is simple (although awkward):

List<Direction> directionsWithNullValues = Lists.newArrayList((Direction)null, null, null);

Java Compiler Warnings 1

Since out of the 5 million Java programmers there's hardly 100 that actually know Java (I do not count myself in that holy hundred), we probably have to join our efforts (like people do at javaranch and help each other.

Here I'm going to post typical Java Compiler warnings that I encounter and fix.

So this chain letter is something like "effective java for dummies", so to say.

1. Creating generic arrays.

Out of your best intentions, you decide that instead of manually concatenating your collections, you'll have something like this:

Collection<T> concat(Collection<T>... collections) {
Collection<T> result = new ArrayList<T>();
for (Collection<T> collection : collections) {
result.addAll(collection):
}
return result;
}


Let's forget the dubious decision of making a live arraylist out of collections of unknown nature, instead of just having a lazy collection or iterable. What's the problem here? Will our compiler complain? No. But if we try to use it, like this:

Collection<Integer> newCollection(oldCollection1, oldCollection2);


we will get a compiler warning: "generic array creation" warning. What's wrong? Here's what. When you pass around a vararg list of parameters, implicitly an array is being created. But Java does not like creating arrays of elements which type is generic. Hence the warning.

To avoid, we can just write a simpler version:

Collection<T> concat(Collection<T> collection1, Collection<T> collection2) {
Collection<T> result = new ArrayList<T>(collection1);
result.addAll(collection2):
return result;
}
...
Collection<Integer> newCollection(oldCollection1, oldCollection2);


Not that I neither condone nor discuss the idea of building an arraylist - this deplorable issue is just outside of the narrow boundaries of this topic.

Followers

Subscribe To My Podcast

whos.amung.us