As I already hinted with my comment above, the question really depends on how exactly you define "support." In your question, you already mentioned that Java is Turing-complete, and therefore Java "supports" (for some definition of "supports") anything that supports any other programming language.
Java supports anonymous functions: just write an interpreter for & lambda; -calculus in Java and pass the anonymous function as a string.
However, I find that there is too much work to use an anonymous function. So, for me, an interesting question is not so much whether Java supports anonymous functions, but whether I want to use anonymous functions, Java supports me. IOW: allows Java to simplify the use of anonymous functions, does it help me, does it help me?
Make a simple experiment: implement the map function and use it to increase each element of the list [1, 2, 3, 4, 5] by 1 .
Haskell
Here, how the morph implementation (that I'm going to call the function so as not to interfere with the existing built-in map function), looks like in Haskell:
morph _ [] = [] morph f (x:xs) = fx : morph f xs
It's all. Short and clear: converting an empty list with anything is just an empty list, and morphing a list with at least one element applies the morph function to the first element and combines it with the result of converting the rest of the list.
As you can see, writing a function that takes a function as an argument is very simple and very easy.
Assuming we have a list l :
l = [1, 2, 3, 4, 5]
Now we can name morph as follows:
morph (\x -> 1 + x) l
Again, passing an anonymous function to our higher order function is very simple, very easy.
And it almost looks like lcalda. In fact, if you use the Haskell IDE, a Haskell-mode text editor, or the pretty-Haskell printer, it will actually display as follows:
morph (λx → 1 + x) l
It becomes even simpler if we use the operator section, which allows us to execute the partially applied statement:
morph (1+) l
Or we can pass a predefined succ function that returns the successor of an integer:
morph succ l
Although this, of course, is not an anonymous function, it is a named one.
Scala
In Scala, it looks very similar. The main difference is that a Scala type system is more complex than Haskell, and therefore requires more type annotations:
def morph[A, B](l: List[A])(f: A => B): List[B] = l match { case Nil => Nil case x :: xs => f(x) :: morph(xs)(f) }
It is still very light. In fact, all we had to do was declare the parameter f type A => B (that is, a function from type A to enter B ), which is actually the syntactic sugar for Function1[A, B] .
Now we just need our list:
val l = List(1, 2, 3, 4, 5)
And change it:
morph(l) {_ + 1}
This again takes advantage of Scala syntactic sugar. In anonymous functions you can leave a list of parameters; if you use each parameter exactly once and in the order in which they are defined, you can simply refer to them as _ .
But even the full form is not much heavier:
morph(l) {(e) => e + 1}
If I ran into the problem of creating a morph instance method of some class and defined an implicit conversion from List to this class according to the Pimp My Library pattern, I could even write something like
l morph {_ + 1}
Scheme
The scheme, of course, should not have problems with anonymous and higher functions. Here morph :
(define (morph fl) (if (null? l) null (cons (f (first l)) (morph f (rest l)))))
Here is our list:
(define l '(1 2 3 4 5))
And using our anonymous function:
(morph (lambda (e) (+ e 1)) '(1 2 3 4 5))
ruby
module Enumerable def morph [].tap {|r| each {|e| r << yield(e) }} end end
It is very light weight. We didn’t even need to define a parameter for a function, because in Ruby every method has an implied function parameter called a block.
l = [1, 2, 3, 4, 5]
Challenge: It's almost as light as Scala
l.morph {|e| e + 1 }
I can sort the operator sections from the Haskell example by capturing a reference to the + 1 method:
l.morph(&1.method(:+))
Ruby also has a predefined succ method for integers, which we can pass with the Symbol#to_proc :
l.morph(&:succ)
Some people criticize blocks in Ruby because each method can only accept one block, and methods that take more than one function are much uglier, but in reality it is not so bad. Here is the same code as above, but without using blocks:
module Enumerable def morph(f) [].tap &-> r { each &-> e { r << f.(e) }} end end l = [1, 2, 3, 4, 5] l.morph -> e { e + 1 } l.morph(1.method(:+))
ECMAScript
ECMAScript is a direct descendant of Scheme, so it is not surprising that it can handle our problem beautifully, albeit with some syntax noise:
Array.prototype.morph = function (f) { var r = []; this.forEach(function (e) { r.push(f(e)); }); return r; }
The main distraction here is usually the ugly syntax, not the processing of higher-order functions.
Let's build our list (well, an array):
var l = [1, 2, 3, 4, 5];
And call the morph function (actually, the method in this case), passing an anonymous function as an argument:
l.morph(function (e) { return e + 1; });
WITH#
Now we are approaching our final language. Here's C #:
public static IEnumerable<B> Morph<A, B>(this IEnumerable<A> l, Func<A, B> f) { IList<B> r = new List<B>(); foreach (var e in l) r.Add(f(e)); return r; }
Not so bad. Pay attention to the type of function: Func<A, B> . This is a predefined type that is part of the main library, like Function1[A, B] in Scala or a → b in Haskell. (This is an important difference from Java.)
Thanks to the input and type initializers, creating a list is not too painful:
var l = new List<int> { 1, 2, 3, 4, 5 };
And passing a lambda that consists of just one expression is basically as easy as Ruby, Scala, Scheme or Haskell and even easier than ECMAScript because you don't need function or return keywords
l.Morph(e => e + 1);
But even using the "full" syntax is not so bad:
l.Morph((e) => { return e + 1; });
(You will notice that I made the morph extension method, which means that I can name it as l.Morph(f) in addition to Morph(l, f) .)
Java
static <A, B> List<B> morph(List<A> l, Function1<A, B> f) { List<B> r = new ArrayList<B>(); for (A e: l) r.add(f.apply(e)); return r; }
At first glance, this is not so bad, actually. In fact, it looks about the same as the C # version. But why can't I write f(e) ? Why should I write f.apply(e) ? In all other languages, I could use the same (or in the case of Ruby, almost the same) syntax to call the function that was passed as an argument, as well as to call any other function, procedure or method.
I know this is a little, but it leaves such a bitter taste that some functions are not quite first-class. In addition, as we will see later, at every step along this path there is one of these small irritations, and although each of them is insignificant in itself, they nevertheless add up.
Here is our list:
List<Integer> l = Arrays.asList(1, 2, 3, 4, 5);
And here is what we call our morph :
morph(l, new Function1<Integer, Integer>() { public Integer apply(Integer n) { return n + 1; } });
This is pretty heavy stuff. I mean, all I do is call the method and pass two arguments. Why does it explode on four lines? In all other languages, it was just a simple liner. Of course, I could remove all line breaks, and he would still be right:
morph(l, new Function1<Integer, Integer>() { public Integer apply(Integer n) { return n + 1; }});
But I think you see what I get. The actual operation performed by increasing each element by 1 is virtually invisible between all these interferences.
Please also note that in some other languages I actually used anonymous functions inside the morph function definition, for example, in Ruby and ECMAScript, and that didn't matter. If I did this in Java, it would lead to even more clutter and crack and crack lines.
So, even at this stage, we see that working with higher and anonymous functions in Java is more cumbersome than in most other main (and not so common) languages.
But we have not yet reached the really ugly part: what is there like Function1<A, B> there? Where did it come from?
Well, I really had to write this type myself!
interface Function1<A, B> { B apply(A a); }
This, of course, is the so-called SAM interface, i.e. interface or abstract class with one abstract method. Which is closest to a function like Java. In a sense, a function is just an object with a single method, so this is great. The fact that function types are represented via SAM interfaces is also not a problem. In fact, basically, as they are presented in Scala (in Scala, f(a) is just syntactic sugar for f.apply(a) , so any object with the apply method is essentially a function), Ruby (in Ruby, f.(a) is just syntactic sugar for f.call(a) , so each object using the call method is essentially a function) and similarly in C #.
The problem is that I had to write it so that it was not there.
Not only did I have to write it myself, I had to come up with a name for it. And I had to come up with a name for the method. None of what I had to do with any of the other languages here. Actually, I just stole the names from Scala, so the actual part of "coming up with the names" was not so complicated.
What really matters is the need to come up with a name. Java has a naming type system, that is, a naming based type system. And thus, the fact that I had to come up with a name means that everyone else should also come up with names. And since their names are different from mine (and if not, it will be a compilation error), this means that I can not pass the same function to two different libraries. Say, for example, I want to pass the same filtering function to gridview and to my ORM. But gridview expects, say, a javax.swing.Predicate<T> with a single apply(T el) method, while my ORM expects org.sleepy.Predicate<T> with a single apply(T el) method.
Please note that these two types are really the same, they just have different names and, therefore, I can not pass the same function to both libraries. This is not a hypothetical example. During recent discussions of the Lambda Java project, someone calculated how many duplicate instances of type Predicate already in Java SE 6, and IIRC in double digits.
It is perfectly possible to solve this problem in a nominal type system. After all, there are dozens of incompatible copies of the List type, simply because Sun puts one in the library, and everyone uses it. They could do the same with function , but they did not, which leads to the spread of the same, but incompatible types, not only in third-party libraries, but even in the JRE. (For example, Runnable is probably a function type, like Comparator . But why should they be specially cropped?) In .NET, this works fine because Microsoft placed one single type at runtime. (Well, actually not exactly one type, but close enough.)
Since there is no single type of function in the JRE, there are also very few methods that accept a type of function. This is another thing that makes first-class and anonymous functions difficult to use in Java. Once you have it, you cannot handle it. You cannot filter the array using the predicate function, you cannot convert the list using the map function, you cannot sort the gridview using the comparator function.
This is also one of the reasons why I am so disappointed with some iterations of Project Lambda. They continue to refuse to introduce functional types from the project, although the lack of function types is IMHO one of the biggest problems. The ugly syntax can be eliminated with the help of an IDE trick, the lack of standard function types cannot. (Not to mention people who use the JVM and JRE but don’t use Java. They don’t use one bit of adding syntactic sugar for anonymous SAM internal classes to the Java language, simply because they don’t use Java because they need function types and an updated collection library that uses function types.)
So, now we have up to four problems:
- syntax overhead because you have to use things like
f.apply(a) and new Function1<A, A>() { public A apply(A a) { return a; }} new Function1<A, A>() { public A apply(A a) { return a; }} (BTW, that is an identity function, that is, a function that does absolutely nothing and takes 58 characters (!)), - modeling overhead, because you yourself must define your own types of functions, in addition to the types of domains that you really like,
- limited utility, because once you have created your lambdas, there are actually not many methods that take lambdas as arguments and
- limited reuse, because even if you find a method that accepts lambda, it does not accept your lambda, since the types do not match.
And when I talk about “overhead”, I'm not just talking about one type of Function1 . Enter primitive types & hellip;
Did you notice how I used Integer , not int in my code above? Yes, that's right, the biggest tan of design in the history of programming languages, Java Original Sin, the curse of every Java programmer's existence, has come again to bite us in the ass: primitive types.
You see that there is exactly one class in Scala that represents a function with arguments n . It is called FunctionN[T₁, T₂, …, Tn, R] . Thus, for functions without any arguments, there is exactly one class Function0[R] , one class Function1[T, R] for functions with one argument, one class Function3[A, B, C, R] for functions with three arguments etc., up to about 20, I believe.
In C # there are exactly two classes that represent a function with arguments n : Func<T₁, T₂, …, Tn, R> and Action<T₁, T₂, …, Tn> . This is because there is no type that represents “no type”. Thus, you cannot declare a function that returns nothing using C # ( void is a modifier, not a type), and therefore you need a separate type ( Action ) to represent functions that return nothing. So, you have two classes Func<R> and Action , which are functions that take no arguments, two classes Func<T, R> and Action<T> , which are functions of one argument, etc. , Again until about 20. (In Scala, a function that returns nothing has a return type of Unit , so you can just have Function2[Int, Int, Unit] , for example.)
In Java, however, you need 10 and times; 9 n types to represent a function of n arguments. Let me demonstrate this with just one argument:
interface Action1_T { void apply(T a); } interface Action1_byte { void apply(byte a); } interface Action1_short { void apply(short a); } interface Action1_int { void apply(int a); } interface Action1_long { void apply(long a); } interface Action1_float { void apply(float a); } interface Action1_double { void apply(double a); } interface Action1_boolean { void apply(boolean a); } interface Action1_char { void apply(char a); } interface Function1_T_R { R apply(T a); } interface Function1_T_byte { byte apply(T a); } interface Function1_T_short { short apply(T a); } interface Function1_T_int { int apply(T a); } interface Function1_T_long { long apply(T a); } interface Function1_T_float { float apply(T a); } interface Function1_T_double { double apply(T a); } interface Function1_T_boolean { boolean apply(T a); } interface Function1_T_char { char apply(T a); } interface Function1_byte_R { R apply(byte a); } interface Function1_byte_byte { byte apply(byte a); } interface Function1_byte_short { short apply(byte a); } interface Function1_byte_int { int apply(byte a); } interface Function1_byte_long { long apply(byte a); } interface Function1_byte_float { float apply(byte a); } interface Function1_byte_double { double apply(byte a); } interface Function1_byte_boolean { boolean apply(byte a); } interface Function1_byte_char { char apply(byte a); } interface Function1_short_R { R apply(short a); } interface Function1_short_byte { byte apply(short a); } interface Function1_short_short { short apply(short a); } interface Function1_short_int { int apply(short a); } interface Function1_short_long { long apply(short a); } interface Function1_short_float { float apply(short a); } interface Function1_short_double { double apply(short a); } interface Function1_short_boolean { boolean apply(short a); } interface Function1_short_char { char apply(short a); } interface Function1_int_R { R apply(int a); } interface Function1_int_byte { byte apply(int a); } interface Function1_int_short { short apply(int a); } interface Function1_int_int { int apply(int a); } interface Function1_int_long { long apply(int a); } interface Function1_int_float { float apply(int a); } interface Function1_int_double { double apply(int a); } interface Function1_int_boolean { boolean apply(int a); } interface Function1_int_char { char apply(int a); } interface Function1_long_R { R apply(long a); } interface Function1_long_byte { byte apply(long a); } interface Function1_long_short { short apply(long a); } interface Function1_long_int { int apply(long a); } interface Function1_long_long { long apply(long a); } interface Function1_long_float { float apply(long a); } interface Function1_long_double { double apply(long a); } interface Function1_long_boolean { boolean apply(long a); } interface Function1_long_char { char apply(long a); } interface Function1_float_R { R apply(float a); } interface Function1_float_byte { byte apply(float a); } interface Function1_float_short { short apply(float a); } interface Function1_float_int { int apply(float a); } interface Function1_float_long { long apply(float a); } interface Function1_float_float { float apply(float a); } interface Function1_float_double { double apply(float a); } interface Function1_float_boolean { boolean apply(float a); } interface Function1_float_char { char apply(float a); } interface Function1_double_R { R apply(double a); } interface Function1_double_byte { byte apply(double a); } interface Function1_double_short { short apply(double a); } interface Function1_double_int { int apply(double a); } interface Function1_double_long { long apply(double a); } interface Function1_double_float { float apply(double a); } interface Function1_double_double { double apply(double a); } interface Function1_double_boolean { boolean apply(double a); } interface Function1_double_char { char apply(double a); } interface Function1_boolean_R { R apply(boolean a); } interface Function1_boolean_byte { byte apply(boolean a); } interface Function1_boolean_short { short apply(boolean a); } interface Function1_boolean_int { int apply(boolean a); } interface Function1_boolean_long { long apply(boolean a); } interface Function1_boolean_float { float apply(boolean a); } interface Function1_boolean_double { double apply(boolean a); } interface Function1_boolean_boolean { boolean apply(boolean a); } interface Function1_boolean_char { char apply(boolean a); } interface Function1_char_R { R apply(char a); } interface Function1_char_byte { byte apply(char a); } interface Function1_char_short { short apply(char a); } interface Function1_char_int { int apply(char a); } interface Function1_char_long { long apply(char a); } interface Function1_char_float { float apply(char a); } interface Function1_char_double { double apply(char a); } interface Function1_char_boolean { boolean apply(char a); } interface Function1_char_char { char apply(char a); }
This 90 (!) Different types simply represents the concept of "something that takes one argument."
And of course, if I want to write something that takes a function as an argument, I also need to have the appropriate number of overloads, so if I want to write a method that filters some values based on a predicate, I need 9 overloads of this function, which accept Function1_T_boolean , Function1_byte_boolean , Function1_short_boolean , Function1_int_boolean , Function1_long_boolean , Function1_float_boolean , Function1_double_boolean , Function1_boolean_boolean and Function1_char_boolean .
(By the way, this still ignores checked exceptions. Technically, we also need 2 n copies of each of these 90 interfaces, where n is the number of different types of checked exceptions that exist in Java.)
So, these are reasons No. 5 and 6: a massive explosion of the number of types and, accordingly, the number of methods.
If you put all this together, then I think you will agree that anonymous inner classes in Java are much more cumbersome than anonymous functions, mostly in any other programming language.
But more!
We did not even talk about closing! Although closures are orthogonal to first-class and anonymous functions, they are also one of the most important (and interesting) uses of anonymous and first-class functions. Inner classes (anonymous or not) are closures, but as @Jon Skeet already pointed out, they are very limited.
In conclusion, I would say that No, Java does not support anonymous functions.
Oh, I almost forgot: somewhere there was another question:
What is actually an anonymous function
It is easy. Function without a name.
and how can you say that some language supports anonymous functions?
If it simplifies working with them.
In Java, no: there is a difference between support for anonymous functions and the ability to emulate a subset of the functions of anonymous functions by encoding them into anonymous internal SAM classes.