I am learning Java 8 and I am trying to use lambdas and generics together and I wrote this small example
import java.util.function.*;
public class LambdaTest<T> {
public T calculate(T x, T y, BiFunction<T, T, T> func) {
return func.apply(x, y);
}
public static void main(String args[]) {
LambdaTest<Integer> l = new LambdaTest<Integer>();
System.out.println("" + l.calculate(10, 10, (x, y) -> x + y));
System.out.println("" + l.calculate(10, 10, (x, y) -> x * y));
System.out.println("" + l.calculate(10, 10, (x, y) -> x / y));
System.out.println("" + l.calculate(10, 10, (x, y) -> x - y));
LambdaTest<Double> l2 = new LambdaTest<Double>();
System.out.println("" + l2.calculate(10.0, 10.0, (x, y) -> x + y));
}
}
Few questions I have are
My lambdas are being defined twice (x, y) -> x + y
. is it possible to define these only once.
It seems that everytime this code is run, it will box 10 to Integer and then run the code. is it possible that I can define this for int
rather than Integer
. I tried doing new LambdaTest<int>
but it did not work.
BiFunction<Integer, Integer, Integer>
, whereas the other is of type BiFunction<Double, Double, Double>
. They're thus not compatible with each other. No it is not possible because the lambda are actually different. In the first one, both x
and y
are of type Integer
, whereas in the second one, both x
and y
are of type Double
. Unfortunately, Integer
and Double
are both Number
and the +
operation is not defined for general Number
.
It is also not possible to use a primitive type with generics. However, you could use IntBinaryOperator
: this is a functional interface operating on two int
values and returning an int
value.
For summing you can use a reference to the corresponding method:
System.out.println("" + l.calculate(10, 10, Integer::sum));
System.out.println("" + l2.calculate(10.0, 10.0, Double::sum));
Here you can see that it's actually not the same code, just similar.
As for boxing, for float/double types the JIT compiler can inline your lambda, see that boxed value does not escape and eliminate the boxing. For example, let's consider such method:
public static void testCalculate(double a, double b) {
LambdaTest<Double> l = new LambdaTest<Double>();
double d = l.calculate(a, b, (x, y) -> x + y);
System.out.println(d);
}
The x64 assembler starts like this:
# {method} {0x0000000051710670} 'testCalculate' '(DD)V' in 'LambdaTest'
# parm0: xmm0:xmm0 = double
# parm1: xmm1:xmm1 = double
# [sp+0xa0] (sp of caller)
mov %eax,-0x6000(%rsp)
push %rbp
sub $0x90,%rsp ;*synchronization entry
; - LambdaTest::testCalculate@-1 (line 10)
vaddsd %xmm1,%xmm0,%xmm2 ;*dadd
; - LambdaTest::lambda$testCalculate$0@8 (line 11)
; - LambdaTest$$Lambda$1/1555845260::apply@8
; - LambdaTest::calculate@3 (line 6)
; - LambdaTest::testCalculate@24 (line 11)
movabs $0x8c900c78,%r10 ; {oop(a 'java/lang/Class' = 'java/lang/System')}
mov 0x6c(%r10),%r10d
mov (%r10),%rax ; implicit exception: dispatches to 0x0000000005bd3426
mov %rax,%r11
and $0x7,%r11
mov %r10,%r9 ;*getstatic out
; - LambdaTest::testCalculate@35 (line 12)
... the rest is inlined code from PrintStream.println(double);
So as you can see, the whole lambda call and boxing is unwinded and simplified to single vaddsd
instruction. No calls, no boxing, no memory access, just two xmm registers. Really fast.
Unfortunately for Integer/Long
the resulting assembler is much less clean due to caching of some values. The JIT compiler cannot just replace this with simple add
, because it's not known exactly that, for example, for cached Integer.valueOf(10)
you will actually get the primitive 10
value. Who knows, probably you replaced some values using the reflection to make 2+2=5
, for example. Nevertheless new objects are not created: if the arguments absent in cache, then new Integer
is actually not called.
- My lambdas are being defined twice (x, y) -> x + y. is it possible to define these only once.
You cannot avoid repeating (x, y) -> x + y
however you can avoid creating two LambdaTest instances .
Actually you don't need any LambdaTest instance when using a static generic function instead of a generic class. Here the code:
import java.util.function.BiFunction;
public class LambdaTest2 {
public static <T> T calculate(T x, T y, BiFunction<T, T, T> func) {
return func.apply(x, y);
}
public static void main(String args[]) {
System.out.println("" + calculate(10, 10, (x, y) -> x + y));
System.out.println("" + calculate(10, 10, (x, y) -> x * y));
System.out.println("" + calculate(10, 10, (x, y) -> x / y));
System.out.println("" + calculate(10, 10, (x, y) -> x - y));
System.out.println("" + calculate(10.0, 10.0, (x, y) -> x + y));
}
}
- It seems that everytime this code is run, it will box 10 to Integer and then run the code. is it possible that I can define this for int rather than Integer. I tried doing new LambdaTest but it did not work.
As already explained by jb-nizet:
To avoid the unboxing and the boxing, you would have to use DoubleBinaryOperator and IntBinaryOperator, which use primitive types. But then you would need two different interfaces.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.