Can someone explain what a functor is and provide a simple example?
相关问题
- Delete Messages from a Topic in Apache Kafka
- Jackson Deserialization not calling deserialize on
- How to maintain order of key-value in DataFrame sa
- StackExchange API - Deserialize Date in JSON Respo
- Difference between Types.INTEGER and Types.NULL in
Take concept of function application
Inverse
Call
x
a functorFrom every-time checks, to Functors, to Java 8 Lambdas (sort of)
The problem
Take this example class, which adapts an Appendable into a Writer:
Not all
Appendable
s areFlushable
orCloseable
, but those that are, must also be closed and flushed. The actual type of theAppendable
object must therefore be checked in every call toflush()
andclose()
and, when it is indeed that type, it is casted and the function is called.Admittedly, this isn't the greatest example, since
close()
is only called once per-instance, andflush()
isn't necessarily called that often either. Also,instanceof
, while reflective, is not too bad given this particular example-usage. Still, the concept of having to check something everytime you need to do something else is a real one, and avoiding these "every-time" checks, when it really matters, provides significant benefits.Move all "heavy duty" checks to the constructor
So where do you start? How do you avoid these checks without compromising your code?
In our example, the easiest step is to move all
instanceof
checks to the constructor.Now that these "heavy duty" checks are done only once, only boolean checks need to be done by
flush()
andclose()
. While certainly an improvement, how can these in-function checks be eliminated entirely?If only you could somehow define a function which could be stored by the class and then used by
flush()
andclose()
...But passing functions is not possible...at least not until Java 8 Lambdas. So how do you do it in pre-8 versions of Java?
Functors
With a Functor. A Functor is basically a Lambda, but one that is wrapped in an object. While functions cannot be passed into other functions as parameters, objects can. So essentially, Functors and Lambdas are a ways to pass around functions.
So how can we implement a Functor into our writer-adapter? What we know is that
close()
andflush()
are only useful withCloseable
andFlushable
objects. And that someAppendable
s areFlushable
, someCloseable
, some neither, some both.Therefore, we can store a
Flushable
andCloseable
object at the top of the class:The "every-time" checks have now been eliminated. But when the
Appendable
is not aFlushable
or not aCloseable
, what should be stored?Do nothing Functors
A do nothing Functor...
...which can be implemented as an anonymous inner class:
To be most efficient, these do-nothing functors should be implemented as static final objects. And with that, here is the final version of our class:
This particular example comes from this question on stackoverflow. A fully working, and fully-documented version of this example (including a testing function) can be found at the bottom of that question-post (above the answer).
Implementing Functors with an Enum
Leaving our
Writer
-Appendable
example, let's take a look at another way to implement Functors: with an Enum.As an example, this enum has a
move
function for each cardinal direction:Its constructor requires a
MoveInDirection
object (which is an interface, but could also be an abstract class):There are naturally four concrete implementations of this interface, one per direction. Here is a trivial implementation for north:
Using this Functor is done with this simple call:
Which, in our example, outputs this to the console:
And here is a full working example:
Output:
I haven't started with Java 8 yet, so I can't write the Lambdas section yet :)
A function object is just that. Something which is both an object and a function.
Aside: calling a function object a "functor" is a serious abuse of the term: a different kind of "functors" are a central concept in mathematics, and one that has a direct role in computer science (see "Haskell Functors"). The term is also used in a slightly different way in ML, so unless you are implementing one of these concepts in Java (which you can!) please stop using this terminology. It makes simple things complicated.
Back to the answer: Java does not have "first class functions" that is to say, you can not pass a function as an argument to a function. This true at multiple levels, syntactically, in the byte code representation, and in that the type system lacks the "function constructor"
In other words, you can't write something like this:
This is really annoying, since we want to be able to do things like have Graphical User Interface libraries where you can associate a "callback" function with clicking on a button.
So what do we do?
Well, the general solution (discussed by the other posters) is to define an interface with a single method that we can call. For example, Java uses an interface called
Runnable
for these kinds of things all the time, it looks like:now, we can rewrite my example from above:
Obviously, this example is contrived. We could make this code a little bit nicer using anonymous inner classes, but this gets the general idea.
Here is where this breaks down:
Runnable
is only usable for functions that don't take any arguments, and don't return anything useful, so you end up defining a new interface for each job. For example, the interfaceComparator
in Mohammad Faisal's answer. Providing a more general approach, and one that takes syntax, is a major goal for Java 8 (The next version of Java), and was heavily pushed to be included in Java 7. This is called a "lambda" after the function abstraction mechanism in the Lambda Calculus. Lambda Calculus is both (perhaps) the oldest programming language, and the theoretical basis of much of Computer Science. When Alonzo Church (one of the main founders of computer science) invented it, he used the Greek letter lambda for functions, hence the name.Other languages, including the functional language (Lisp, ML, Haskell, Erlang, etc), most of the major dynamic languages (Python, Ruby, JavaScript, etc) and the other application languages (C#, Scala, Go, D, etc) support some form of "Lambda Literal." Even C++ has them now (since C++11), although in that case they are somewhat more complicated because C++ lacks automatic memory management, and won't save your stack frame for you.
A functor is an object that's a function.
Java doesn't have them, because functions aren't first-class objects in Java.
But you can approximate them with interfaces, something like a Command object:
Updated on 18-Mar-2017:
Since I first wrote this JDK 8 has added lambdas. The java.util.function package has several useful interfaces.