Saturday, March 29, 2008

Method references (Implementation Issue)

Method references (Implementation Issue)

Each method reference is transformed to a closure:

  // Math#sqrt(double) will be transformed to { double d => Math.sqrt(d) }
  { double => double } sqrt = Math#sqrt(double);
  

Instance method references are transformed in a similar way.

  Circle c = new Circle();
  { => void } p = c#draw();  // the same as { => c.draw(); }
  
  { Circle => void } p = Circle#draw();  // { Circle c => c.draw(); }
  

Let us have a look at another example. We declare class Box:

  public class Box<T> {
    T v;
    public Box(T v) {
      this.v = v;
    }
    public T getValue() {
      return v;
    }
    public void print() {
      System.out.println("Box: " + v);
    }
  }
  

and refer to the print method:

  // Box#print() will be transformed to { Box<Integer> box => box.print(); }
  { Box<Integer> => void } p = Box#print();
  

Reference to a generic method will be replaced by a closure as follows:

  public class Generic {
    public static <T> Box<T> copy(Box<T> b) {
        return new Box<T>(b.getValue());
    }
  }
  
  // Generic#copy(Box<Integer>) will be transformed to 
  // { Box<Integer> box => Generic.copy(box) }
  { Box<Integer> => Box<Integer> } boxCopy = Generic#copy(Box<Integer>);
  

I do not want closures in Java because they are not simple.
This argument is wrong. We should always weigh all pros and cons. Closures are not simple. But is software development in Java simple? What should I know to be capable to develop software in Java? Knowledge of the Java programming language is clearly not enough. One should also know some Java technologies and frameworks. And are they simple? Are JPA, EJB, JAX-WS, Hibernate, or Spring (to name a few) simple? Apparently no, so it does not make sense to reject closures just for this reason.

Thursday, March 20, 2008

Method References (version 2008-03-17)

Method references (version 2008-03-17)

The compiler prototype (available at http://www.javac.info) comes with method references (also known as eta expansion). The method references are written as follows: ClassName # methodName ( listOfArgumentTypes ).

A method reference can be assigned to a function variable:

  { String => int } parseInt = Integer#parseInt(String);
  int x = parseInt.invoke("42");
  

We can use the covariant return:

  // Integer.valueOf() returns Integer
  { int => Number } p = Integer#valueOf(int);
  System.out.println(p.invoke(97));
  

And the contravariant arguments:

  class MyClass {
    static Integer print(Object o) {
      return Integer.valueOf(o.hashCode());
    }
    
    public static void main(String[] args) {
      { String => Number } pp = MyClass#print(Object);
      System.out.println(pp.invoke("hi"));
    }
  }
  

An instance method can be referenced in two ways. Either we reference a method on a given object:

  class Box {
    private int x;
    Box(int x) {
        this.x = x;
    }
    int getX() {
        return x;
    }
  }
  public class InstanceMethod {
    public static void main(String[] args) {
      Box p = new Box(10);
      { => int } getX = p#getX();
      System.out.println(getX.invoke());
    }
  }  
  

Or we reference just a method. Then the function type has an additional argument: the object on which the method is called.

  { Box => int } getX = Box#getX();  // additional argument of type Box
  Box p = new Box(10);
  System.out.println(getX.invoke(p));
  

The method is selected at runtime according to the object supplied as argument. So, in the following example, the toString method from String is called.

  { Object => String } toString = Object#toString();
  System.out.println(toString.invoke("hi"));
  

Generic methods are also supported:

  { String => Set<String> } singleton =
      Collections#<String>singleton(String);
  Set<String> set = singleton.invoke("single");