6

In Java 8, there are many functional interfaces provided such as UnaryOperator, BinaryOperator and Function etc.

The Code,

UnaryOperator<Integer> uOp = (Integer i) -> i * 10;
BinaryOperator<Integer> bOp = (Integer i1, Integer i2) -> i1 * i2 * 10;

can always be written using Function as below,

Function<Integer, Integer> f1 = (Integer i) -> i * 10;
BiFunction<Integer, Integer, Integer> f2 = (Integer i1, Integer i2) -> i1 * i2 * 10;

So, whats the use of these operator interfaces ? Are they achieving anything different than what can be achieved using Function ?

Razzle
  • 479
  • 4
  • 11
Shailesh Pratapwar
  • 4,054
  • 3
  • 35
  • 46

4 Answers4

4

They are here for your convenience. You can spare writing BiFunction<Integer, Integer, Integer> and just write/use a BinaryOperator<Integer> instead. An additional benefit: you can ensure that the function that is given to you accepts 1 or two parameters of the same type and return exactly that type without much more writing.

Additionally due to the nature of BinaryOperator<T> it makes more sense to put something like minBy and maxBy there, which doesn't really make so much sense to put into a BiFunction<T, U, R>. As the given parameters there are of same type and the return type is ensured also to be the same, a comparator can be easily applied... very convenient.

Roland
  • 22,259
  • 4
  • 57
  • 84
  • Since `minBy` and `maxBy` are `static` methods *returning* a `BinaryOperator`, they would simply return a `BiFunction`, without loosing any functionality. – Holger Jan 30 '19 at 13:59
  • true... the convenience (simplicity and certainty) that these are guaranteed to be the same types is probably the more important point. – Roland Jan 30 '19 at 14:05
4

Functional interfaces should be specialised as possible.

Having

Function<Integer, Integer> f1 = (Integer i) -> i * 10;

Instead of:

UnaryOperator<Integer> uop1 = (Integer i) -> i * 10;

is actually a code smell (there is also Sonar Rule squid:S4276 for this).

The simple reason for this is that these interfaces were created to avoid passing unnecessary type parameters n times while you have only one.

public interface UnaryOperator<T> extends Function<T, T>

So writing a Function<T, T> is just longer and unnecessary.

Talking about other interfaces like: IntConsumer vs. Consumer<Integer> or DoubleToIntFunction vs. Function<Double, Integer> where the second option may lead to unnecessary auto-boxing and may downgrade performance.

So that's why using more specific and appropriate interface makes your code look cleaner and keeps you away from surprises.

J-Alex
  • 6,881
  • 10
  • 46
  • 64
  • 1
    Most of the time, you will provide a lambda expression where a function is expected, without ever writing down the interface type. The fact that Sonar calls it a code smell, doesn’t say anything, if the `UnaryOperator` did not exist, Sonar did not call using `Function` a code smell. – Holger Jan 30 '19 at 14:09
1

Yes, they're functionally identical. They even extend the classes you're talking about and use the same SAM. The UnaryOperator and BinaryOperator interfaces only define static methods.

public interface UnaryOperator<T> extends Function<T, T>

public interface BinaryOperator<T> extends BiFunction<T,T,T>

They're simply there for brevity. Why specify a type parameter 2 or 3 times when you can do it once?

Michael
  • 41,989
  • 11
  • 82
  • 128
0

UnaryOperator and BinaryOperator are shortcuts for Function and BiFunction when the type of return type is the same as the input type. I think that they also may carry different meannings, an operation and a function may have different intrepretations depending on your context this is essentially for code readablity and not for technical reasons.

Youssef NAIT
  • 1,362
  • 11
  • 27