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");