Not kidding!
Project and code is hosted on GIT hub:
Basically, this library gives you the ability to strictly define a one method interface on the fly with support for the method to take up to 20 defined type paramaters, a defined return type or just as void. After 20 parameters ( who needs that many? ) we can use RU or VU for undefined and unlimited amount of parameters.
Closure.java
It is a one file implementation, for usage simplicity and works as a class container with public and hidden private classes
public final class Closure {
...
/** R0 = Return of defined type with 0 parameters */
public static interface R0<Returns> {
public Returns call();
}
/** R1 = Return of defined type with 1 defined parameters */
public static interface R1<Returns, Param1> {
public Returns call(Param1 p1);
}
/** R2 = Return of defined type with 2 defined parameters */
public static interface R2<Returns, Param1, Param2> {
public Returns call(Param1 p1, Param2 p2);
}
...
}
It's amazing that nobody actually got around to write something so basic and simple!
I have supplied three Closure.java implementations (actually made a couple more but were dismissed), v0 (version 0), v1 and v2.
My favourite of the three is v1 though, but have a look at v2 as well which is a bit more dynamic, whereas v0 and v1 are fully static.
Examples
and usage of Closure.java v1 exists in:
ClosureExamples.java
ClosureExamples uses a small library that currently contains only a few methods :
Lib.java
To run the examples, check out the entire project, open a command prompt, standing at the root directory (where org, and examples directories are)
run:
run:
>> javac examples/v1/ClosureExamples.java
>> java examples.v1.ClosureExamples
Inline examples
package examples.v1;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.mo.closure.v1.Closure;
import org.mo.closure.v1.Closure.R1;
import org.mo.closure.v1.Closure.R2;
public class InlineExamples {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ================================= Each line in a file =====================================
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Example usage of eachLine() above, by passing a Closure.V1<String> and prints out each line in a file
eachLine("C:/file.txt", new Closure.V1<String>() {
public void call(String line) {
if ( line.endsWith("virus") ) System.out.println("virus detected!");
}
});
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final List<String> lst = new ArrayList<String>();
eachLine("C:/file.txt", new V1<String>() {
public void call(String line) {
lst.add(line); // Add line to the list
}
});
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
// METHOD DOWN HERE!
/** Closure.V1<String> => V for void, and 1 for one parameter of type String **/
static void eachLine(String fileName, Closure.V1<String> cloj) {
BufferedReader bfIn = null;
try {
bfIn = new BufferedReader(new FileReader(fileName));
String line = null;
while ((line = bfIn.readLine()) != null) {
cloj.call(line); // !!! Closure called here !!!
}
bfIn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ================================= Find file recursively ===================================
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
findFileRecurse(new File("C:/"), new Closure.R1<Boolean, File>() {
public Boolean call(File file) {
return file.isDirectory() && file.getName().equals("System32");
}
});
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
/** Find file recursively **/
static File findFileRecurse(final File dir, final Closure.R1<Boolean, File> cloj) {
if (cloj.call(dir))
return dir;
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
File foundFile = findFileRecurse(file, cloj);
if (foundFile != null)
return foundFile;
} else if (cloj.call(file)) {
return file;
}
}
}
return null;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ================================= Switching implementation during Runtime ==================
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/** hasValidCredentials method behaviour can be modified by resetting the static property just below */
static boolean hasValidCredentials(String username, String password) {
if (VALID_CREDENTIALS != null) {
return VALID_CREDENTIALS.call(username, password);
}
return false;
}
/**
* STATIC Closure property that can be switched during runtime
* Closure.R2<Boolean, String, String> => R for defined return type of type Boolean, and 2 parameters of type String */
static Closure.R2<Boolean, String, String> VALID_CREDENTIALS = new R2<Boolean, String, String>() {
public Boolean call(String username, String password) {
return "myPassword".equals(password));
}
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//========================================= Find in a collection ==============================
// --------------------------------------------------------------------------------------------
static {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final List<String> names = new ArrayList<String>();
names.add("Anna"); names.add("James"); names.add("Mohamed"); names.add("Sarah");
find(names, new Closure.R1<Boolean, String>() {
public Boolean call(String name) {
return name.equals("Mohamed"); // Will return the instance that mathches this criteria
}
});
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
/**
* Find and return from collection following a boolean criteria.
* Closure in parameter must be the same as the collection type */
static <T> T find(Collection<T> objects, Closure.R1<Boolean, T> cloj) {
for ( T obj : objects) {
if ( cloj.call(obj) ) // If user closure returned true, then object is found
return obj;
}
return null;
}
//============================== Collect a collection ========================================
// -------------------------------------------------------------------------------------------
static class Address {
double longitude, latitude;
String city;
public Address(String city){ this.city = city; }
}
static {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final List<String> names = new ArrayList<String>();
names.add("Anna"); names.add("James"); names.add("Mohamed"); names.add("Sarah");
List<Integer> lengthOfNames = collect(names, new R1<Integer, String>() {
public Integer call(String name) {
return name.length();
}
});
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final List<Address> addresses = new ArrayList<Address>();
addresses.add( new Address("Stockholm")); addresses.add(new Address("London") );
// What if we only wanted the cities in a list? Not the addresses?
List<String> citys = collect(addresses, new Closure.R1<String, Address>() {
public String call(Address address) {
return address.city;
}
});
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
// ===> METHOD HERE <===
/** Allows you return a modified list of each entry.
* TypeIn is defined by the collection.
* TypeOut defined by what the closure implementor chooses to return */
static <TypeIn, TypeOut> ArrayList<TypeOut> collect(Collection<TypeIn> lst, Closure.R1<TypeOut, TypeIn> cloj) {
ArrayList<TypeOut> newLst = new ArrayList<TypeOut>();
for ( TypeIn obj : lst )
newLst.add ( cloj.call(obj) );
return newLst;
}
}
See ClosureExamples.java, Lib.java for more examples
Relevant
Intellij IDEA is preparing for the project lambda, so it will be easy to convert the above to real lambdas:
Project lambda
Current syntax suggestion for the project lambda is pretty ugly, and hard to understand:
http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-4.html
There are too many ways a lambda/closure can be defined, it should be more coherent.
I kind like how it looks like on this old presentation I found:
http://www.slideshare.net/enbohm/project-lambda-4599885
Inline examples
package examples.v1;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import org.mo.closure.v1.Closure;
import org.mo.closure.v1.Closure.R1;
import org.mo.closure.v1.Closure.R2;
public class InlineExamples {
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ================================= Each line in a file =====================================
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// Example usage of eachLine() above, by passing a Closure.V1<String> and prints out each line in a file
eachLine("C:/file.txt", new Closure.V1<String>() {
public void call(String line) {
if ( line.endsWith("virus") ) System.out.println("virus detected!");
}
});
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final List<String> lst = new ArrayList<String>();
eachLine("C:/file.txt", new V1<String>() {
public void call(String line) {
lst.add(line); // Add line to the list
}
});
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
// METHOD DOWN HERE!
/** Closure.V1<String> => V for void, and 1 for one parameter of type String **/
static void eachLine(String fileName, Closure.V1<String> cloj) {
BufferedReader bfIn = null;
try {
bfIn = new BufferedReader(new FileReader(fileName));
String line = null;
while ((line = bfIn.readLine()) != null) {
cloj.call(line); // !!! Closure called here !!!
}
bfIn.close();
} catch (IOException e) {
e.printStackTrace();
}
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ================================= Find file recursively ===================================
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
static {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
findFileRecurse(new File("C:/"), new Closure.R1<Boolean, File>() {
public Boolean call(File file) {
return file.isDirectory() && file.getName().equals("System32");
}
});
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
/** Find file recursively **/
static File findFileRecurse(final File dir, final Closure.R1<Boolean, File> cloj) {
if (cloj.call(dir))
return dir;
File[] files = dir.listFiles();
if (files != null) {
for (File file : files) {
if (file.isDirectory()) {
File foundFile = findFileRecurse(file, cloj);
if (foundFile != null)
return foundFile;
} else if (cloj.call(file)) {
return file;
}
}
}
return null;
}
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
// ================================= Switching implementation during Runtime ==================
// ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/** hasValidCredentials method behaviour can be modified by resetting the static property just below */
static boolean hasValidCredentials(String username, String password) {
if (VALID_CREDENTIALS != null) {
return VALID_CREDENTIALS.call(username, password);
}
return false;
}
/**
* STATIC Closure property that can be switched during runtime
* Closure.R2<Boolean, String, String> => R for defined return type of type Boolean, and 2 parameters of type String */
static Closure.R2<Boolean, String, String> VALID_CREDENTIALS = new R2<Boolean, String, String>() {
public Boolean call(String username, String password) {
return "myPassword".equals(password));
}
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//========================================= Find in a collection ==============================
// --------------------------------------------------------------------------------------------
static {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final List<String> names = new ArrayList<String>();
names.add("Anna"); names.add("James"); names.add("Mohamed"); names.add("Sarah");
find(names, new Closure.R1<Boolean, String>() {
public Boolean call(String name) {
return name.equals("Mohamed"); // Will return the instance that mathches this criteria
}
});
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
/**
* Find and return from collection following a boolean criteria.
* Closure in parameter must be the same as the collection type */
static <T> T find(Collection<T> objects, Closure.R1<Boolean, T> cloj) {
for ( T obj : objects) {
if ( cloj.call(obj) ) // If user closure returned true, then object is found
return obj;
}
return null;
}
//============================== Collect a collection ========================================
// -------------------------------------------------------------------------------------------
static class Address {
double longitude, latitude;
String city;
public Address(String city){ this.city = city; }
}
static {
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final List<String> names = new ArrayList<String>();
names.add("Anna"); names.add("James"); names.add("Mohamed"); names.add("Sarah");
List<Integer> lengthOfNames = collect(names, new R1<Integer, String>() {
public Integer call(String name) {
return name.length();
}
});
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
final List<Address> addresses = new ArrayList<Address>();
addresses.add( new Address("Stockholm")); addresses.add(new Address("London") );
// What if we only wanted the cities in a list? Not the addresses?
List<String> citys = collect(addresses, new Closure.R1<String, Address>() {
public String call(Address address) {
return address.city;
}
});
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
}
// ===> METHOD HERE <===
/** Allows you return a modified list of each entry.
* TypeIn is defined by the collection.
* TypeOut defined by what the closure implementor chooses to return */
static <TypeIn, TypeOut> ArrayList<TypeOut> collect(Collection<TypeIn> lst, Closure.R1<TypeOut, TypeIn> cloj) {
ArrayList<TypeOut> newLst = new ArrayList<TypeOut>();
for ( TypeIn obj : lst )
newLst.add ( cloj.call(obj) );
return newLst;
}
}
See ClosureExamples.java, Lib.java for more examples
Relevant
Intellij IDEA is preparing for the project lambda, so it will be easy to convert the above to real lambdas:
Current syntax suggestion for the project lambda is pretty ugly, and hard to understand:
http://cr.openjdk.java.net/~briangoetz/lambda/lambda-state-4.html
There are too many ways a lambda/closure can be defined, it should be more coherent.
I kind like how it looks like on this old presentation I found:
http://www.slideshare.net/enbohm/project-lambda-4599885
Cool! Exactly what I have been looking for!
ReplyDeleteA bit verbose but incredibly powerful! Nice effort!
ReplyDeleteVery nice! Yes, as someone else said, it's verbose, but Java is verbose sometimes. I'm really looking forward to lambdas in Java 8! Look me up and introduce yourself sometime as I don't immediately see how to find out who you are.
ReplyDeleteIt is as thin as it can be using Java.
DeleteWith Java 7, there is also generic type interference on the right hand side on instance creation so there is no need for repeating the generic part.
Also, using import static org.mo.closure.*;
we could also do
new R1<>(){} rather than new Closure.R1<>(){ } everywhere.
http://functionaljava.org/
ReplyDeleteYes, I have seen the functionaljava library before, and initially this blog post contained a reference to it.
DeleteHave you seen the size of that library, any idea of how to actually use it? It tries to do so much more than just provide Closures to the Java world, yet fails to provide the most basic.
http://functionaljava.googlecode.com/svn/artifacts/3.0/javadoc/index.html
functionaljava.jar 1,339kb
DeleteSize meaning number of classes and too many methods. Functional java tries to be functional, but really I just want Closure support. Java compiled class files is not the way to measure that :)
DeleteFunctional java focus seems to be on doing things like sort, filter, exits, map, foldRight, left and what not.
When you want more than those predefined, it is hard to figure out how to do something like this implementation does.
This one supports passing of non-final variables, voids, unlimited number of parameters if you want.
Functional java is intended for something else, I am not sure it even provides "Closures" or something similar as this to the java world.
Could you show me how I could do an eachLine on a file, eachFileRecure, findFileRecurse that takes a "Closure"? I am unable to figure that part out.
I cant even figure out which class is actually supposed to provide something similar as the implementation here.
http://functionaljava.googlecode.com/svn/artifacts/3.0/javadoc/fj/data/vector/V4.html
http://functionaljava.googlecode.com/svn/artifacts/3.0/javadoc/fj/P5.html
http://functionaljava.googlecode.com/svn/artifacts/3.0/javadoc/fj/F4.html
http://functionaljava.googlecode.com/svn/artifacts/3.0/javadoc/overview-summary.html
You know, the car might already be invented, if that is even the case here(?), but they still build new and better ones.