InfoQ

InfoQ

News

My Bookmarks

Login or Register to enable bookmarks for unlimited time.

The content has been bookmarked!

There was an error bookmarking this content! Please retry.

SAMbdas in Java

Posted by Alex Blewitt on Jul 20, 2010

Sections
Development,
Architecture & Design
Topics
Java ,
Syntax ,
Language
Tags
Lambda Expressions

Since the initial Lambda proposal was released (and the in-depth InfoQ analysis), there has been a subsequent state of the lambda which has significantly moved the goalposts of the lambda project in JDK 7.

There were a number of criticisms of the initial syntax proposed, but the problems ran deeper than pure syntax (which after all, was just a strawman). One of the key issues was that Java has no direct support for function types; and that introduced holes in Java's type system (an array of functions would permit exceptions to leak). Whether these problems were insurmountable or not (or more likely, within the given time-frame for JDK 7's increasingly delayed release), the current state-of-the-lambda has no mention of function types at all.

Instead, we have an adaptation to make it easier to write inner classes. These classes are referred to as SAM classes, or Single Abstract Method classes. This represents a significant subset of abstract classes and interfaces in the Java language that just have a single abstract method; for example, Runnable's run() method, Comparator's compare() method, and so on. (Abstract classes which have a single abstract method are also permitted such as Eclipse's org.eclipse.core.runtime.jobs.Job.)

The current work-in-progress spec suggests that the following would therefore be equivalent:


Collections.sort(list,new Comparator() {
  public int compare(Object o1, Object o2) {
    return(o1.toString().length() - o2.toString().length());
  }
}
// is the same as
Collections.sort(list,
  { Object o1, Object o2 -> o1.toString().length() - o2.toString().length() }
);

Again, the syntax is at the proposal stage so may change in the future, but the idea is that the lambda project will permit inner classes to be written more succinctly than using the new anonymous class approach. Otherwise, the lambdas remain the same expressiveness as inner classes; you can capture state from the local heap (though whether that heap remains mutable post capture is still a hotly debated topic). However, some changes to the language (like being able to capture effectively final variables), and having type and method/exception inference makes it much more concise than the equivalent anonymous class.

One reason for following this approach is to permit existing classes (primarily the java.util collections classes) without having to be modified. Had the function type approach been used, then it would have been necessary to retro-fit the collections classes to take lambdas as well, or forgo their inclusion (and therefore usage) in JDK 7. Whilst other libraries would have been able to be flexible, the monolithic Java libraries aren't so mutable and this may be a reason why a different approach was chosen.

It may also be possible to use method references to substitute for a SAMbda as well. For example, we could have:


public class Comparisons {
  public static int compareLength(Object o1, Object o2) {
    return(o1.toString().length() - o2.toString().length());
  }
  public static int compareHash(Object o1, Object o2) {
    return(o1.hashCode() - o2.hashCode());
  }
}
// examples
Collections.sort(list,#Comparisons.compareLength);
Collections.sort(list,#Comparisons.compareHash);

The # references are method handles, which are similar to java.lang.reflect.Method. However, unlike Method, they are resolved at compile-time (rather than run-time) and the JVM's JIT will have sufficient smarts to be able to in-line the method references automatically. There are also other optimisations that this permits, such as creating a single class to represent delegated method handles for a given SAM type, rather than creating a new anonymous class at the point of use.

Finally, there are contentious issues as well. The initial draft of the current spec prohibited the use of break and continue, but that was subsequently clarified to prevent breaking out of a SAMBda into the enclosing scope. The other key change is that return is implied, and is not allowed in the body of the lambda itself; though a substitute keyword yield (not to be confused with Thread.yield()) has exactly the same semantics as a return would in an inner class. Ostensibly this is to permit a lambda to trigger a return from a method in which it is called later (a so-called 'long return'). There may be a change in the syntax in the future to permit return to be used unadorned in a lambda, and require a subsequent new keyword (or keyword combination, like long return). Other similarities will include this to refer to the enclosing SAM instance, and Outer.this to refer to an instance of the enclosing class.

The changes to the lambda proposal to be a drop-in replacement for SAMs is less ambitious than the project's original proposal, but has the advantage of being easier to implement and not requiring any changes to the existing collections classes in order to be immediately useful. (Changes to the collections classes would have been necessary if function types were added in any case.) It may also be possible to create function references in the future using the same lambda-like syntax, but targeted for a future JDK release.

This post has been updated to reflect the fact that the break/continue has been resolved, and to include the type inference for formal parameters and exception types.

Great news by William H Posted
Re: Great news by prassee sathian Posted
How to do this in old Java by Lukasz Czapski Posted
Re: How to do this in old Java by Sam Pullara Posted
Re: How to do this in old Java by Knox Liam Posted
Re: How to do this in old Java by Lukasz Czapski Posted
Re: How to do this in old Java by Mario Fusco Posted
Re: How to do this in old Java by Lukasz Czapski Posted
No Function Types by Knox Liam Posted
  1. Back to top

    Great news

    by William H

    I must say I’m very relieved. I’m not a language designer by any stretch of the imagination but I was seriously concerned by some of the problems in the previous Lambada specification; This looks much more pragmatic, and whilst it may be less than some had hoped for, is probably the best outcome at this time.

  2. Back to top

    Re: Great news

    by prassee sathian

    In the discussion we missed a project called Lambdaj which provides these features and the java community can concentrate on that project. I believe this project has got a nice base on the
    closure syntax which should be pluggable into the core platform.

  3. Back to top

    How to do this in old Java

    by Lukasz Czapski

    Lately I had similar idea to simplify working with anonymous classes. I use reflection to get rid of with long code that is use to create them.

    Example, before:


    Comparator myComperator = new Comparator() {
    public int compare(Object o1, Object o2) {
    return(o1.toString().length() - o2.toString().length());
    }
    };



    Collections.sort(list, myComperator);


    After

    Comparator myComperator;

    public int myComperator (Object o1, Object o2) {
    return(o1.toString().length() - o2.toString().length());
    }

    // this is executed in constructor
    AnonymousClassCreator.createObjectForAllFields(this);



    Collections.sort(list, myComperator);


    All “magic” is done in AnonymousClassCreator. It creates objects for all fields in given object if there type is known. It uses convention: a method in given class is used that has the same name as field. Here there is method ‘myComperator’ and field ‘myComperator’. So it creates object that implement 'Comparator' that will be using method ‘myComperator’ from given class.
    More info can be found here .

  4. Back to top

    Re: How to do this in old Java

    by Sam Pullara

    Hey, that is a pretty cool idea. Seems like this mechanism could easily be added to a dependency injection framework like Guice.

  5. Back to top

    Re: How to do this in old Java

    by Knox Liam

    Tome this looks very difficult to understand, very fragile and no doubt by passes all the type checking that the compiler would employ.

  6. Back to top

    Re: How to do this in old Java

    by Lukasz Czapski

    Sorry for broken link, now it works .

    It is only idea to establish link between method and field, creating something like pointer. How it would been implemented, it depends how strict it should be. I use reflection and name matching becouse it was easier to make proof. But it could be more fragile, or less flexible. For example using: Annotation for matching, and reflection could be replace something like Lombok .

  7. Back to top

    Re: How to do this in old Java

    by Mario Fusco

    With lambdaj you cannot do exactly the same, but only because it works with a dynamic proxy mechanism that doesn't allow to proxy final classes (like String). However if you want to sort the list of Object based on their String representation you can write something like:

    sort(list, on(Object.class).toString());

    or, for example, if you want to sort a list of cars based on the age of their owners you could write:

    sort(cars, on(Car.class).getOwner().getAge());

    I believe this solution is easier and more readable.

  8. Back to top

    Re: How to do this in old Java

    by Lukasz Czapski

    Lambdaj is more readable.
    It is only a kind of tool to make "closure" in java. Lambdaj is more mature, but it has fixed vocabulary. In that way (where methods are treated as objects) you have to create your own set of operations, which could be more adequate for given domain.
    It is an experiment how it could be done (example).

  9. Back to top

    No Function Types

    by Knox Liam

    Maybe I am missing something but arent lambda bodie equivalent to function types?

    If there is no support for defining a method to take a arbitary matching function signature then the whole proposal is floored as at the root of its demand was for dealing with frameworks such as fork-join where this is exactly what you want

Educational Content

New-age Transactional Systems - Not Your Grandpa's OLTP

John Hugg discusses high volume transaction processing applications with high and low frequency profiles, and how VoltDB can be used for that purpose.

Cool Code

Kevlin Henney examines code samples to see what can be learned from them starting from the premise that one won’t write great code unless he knows how to read it.

Collaboration: At the Extremities of Extreme

Jason Ayers share the observations he made watching a team of developers collaborating in real time on the same code base, pushing XP, pair programming and continuous integration to their extremes.

Yesod Web Framework

Michael Snoyman presents Yesod, a web framework written in Haskell and containing a web server, templating, ORM, libraries (templating, gravatar, etc.).

Transactions without Transactions

Richard Kreuter and Kyle Banker on how to avoid classical RDBMS transactional systems by using compensation mechanisms, transactional messaging or transactional procedures.

Attila Szegedi on JVM and GC Performance Tuning at Twitter

Attila Szegedi talks about performance tuning Java and Scala programs at Twitter: how to approach GC problems, the importance of asynchronous I/O, when to use MySQL/Cassandra/Redis, and much more.

10 tips on how to prevent business value risk

One category of risk that project teams need to ensure they address is business value failure – delivering a product that fails to provide value for the business investor.

Interview: Software Systems Architecture: Working With Stakeholders Using Viewpoints and Perspectives

InfoQ spoke to the authors of Software Systems Architecture on a couple of new topics, the System Context viewpoint and Agile, which have been added to the second edition.