我正在学习Java 8,我正在尝试使用lambdas和泛型,我写了这个小例子
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));
}
}
我的问题很少
我的lambdas被定义了两次(x, y) -> x + y
。是否有可能只定义一次。
似乎每次运行此代码时,它都会将10封装到Integer,然后运行代码。是否有可能为int
而不是Integer
定义此内容。我尝试过做new LambdaTest<int>
但是没有用。
答案 0 :(得分:6)
BiFunction<Integer, Integer, Integer>
类型,而另一个是BiFunction<Double, Double, Double>
类型。因此它们彼此不相容。答案 1 :(得分:5)
不可能,因为lambda实际上是不同的。在第一个中,x
和y
都属于Integer
类型,而在第二个中,x
和y
都属于{{1}类型}}。很遗憾,Double
和Integer
都是Number
,Double
操作未定义为一般+
。
也不可能使用带有泛型的基本类型。但是,您可以使用IntBinaryOperator
:这是一个功能界面,对两个Number
值进行操作并返回int
值。
答案 2 :(得分:2)
对于求和,您可以使用对相应方法的引用:
System.out.println("" + l.calculate(10, 10, Integer::sum));
System.out.println("" + l2.calculate(10.0, 10.0, Double::sum));
在这里你可以看到它实际上不是相同的代码,只是类似。
至于装箱,对于float / double类型,JIT编译器可以内联你的lambda,看看盒装值不会逃脱并消除装箱。例如,让我们考虑这样的方法:
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);
}
x64汇编程序如下所示:
# {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);
正如您所看到的,整个lambda调用和装箱是unwinded并简化为单vaddsd
指令。没有电话,没有装箱,没有内存访问,只有两个xmm寄存器。真的很快。
不幸的是,对于Integer/Long
,由于某些值的缓存,生成的汇编程序更不干净。 JIT编译器不能只用简单的add
替换它,因为它并不确切地知道,例如,对于缓存的Integer.valueOf(10)
,您实际上会获得原始10
值。谁知道,例如,您可能使用反射来替换某些值以生成2+2=5
。然而,不会创建新对象:如果缓存中没有参数,则实际上不会调用new Integer
。
答案 3 :(得分:1)
- 我的lambdas定义了两次(x,y) - &gt; x + y。是否可以只定义一次。
醇>
您无法避免重复(x, y) -> x + y
,但可以避免创建两个LambdaTest实例。
实际上,在使用静态泛型函数而不是泛型类时,不需要任何LambdaTest实例。代码如下:
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));
}
}
- 似乎每次运行此代码时,它都会将10封装到Integer,然后运行代码。是否有可能我可以为int而不是Integer定义它。我尝试了新的LambdaTest,但它没有用。
醇>
正如jb-nizet所说:
要避免拆箱和装箱,您必须使用DoubleBinaryOperator和IntBinaryOperator,它们使用基本类型。但是你需要两个不同的接口。