首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >使用Lambdas和Generics

使用Lambdas和Generics
EN

Stack Overflow用户
提问于 2015-09-17 21:58:25
回答 4查看 148关注 0票数 2

我正在学习Java 8,我正在尝试同时使用lambda和泛型,我编写了这个小示例

代码语言:javascript
复制
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));
    }
}

我有几个问题

  1. 我的lambda被定义了两次(x, y) -> x + y。是否有可能只定义一次。
  2. 似乎每次运行此代码时,它都会将10框放入Integer,然后运行该代码。我是否可以为int而不是Integer来定义它。我试着做new LambdaTest<int>,但是它没有起作用。
EN

回答 4

Stack Overflow用户

回答已采纳

发布于 2015-09-17 22:03:59

  1. 不是的。一个是BiFunction<Integer, Integer, Integer>类型,另一个是BiFunction<Double, Double, Double>类型。因此,它们彼此不相容。
  2. 为了避免取消装箱和装箱,您必须使用DoubleBinaryOperator和IntBinaryOperator,它们使用原语类型。但是,您需要两个不同的接口。
票数 6
EN

Stack Overflow用户

发布于 2015-09-17 22:04:17

  1. 不,这是不可能的,因为兰达实际上是不同的。在第一种情况下,xy都是Integer型,而在第二种类型中,xy都是Double型。不幸的是,IntegerDouble都是Number,没有为通用Number定义+操作。
  2. 也不可能在泛型中使用原语类型。但是,您可以使用IntBinaryOperator:这是一个对两个int值进行操作并返回int值的函数接口。
票数 5
EN

Stack Overflow用户

发布于 2015-09-18 02:26:18

为了求和,可以使用对相应方法的引用:

代码语言:javascript
复制
System.out.println("" + l.calculate(10, 10, Integer::sum));
System.out.println("" + l2.calculate(10.0, 10.0, Double::sum));

在这里您可以看到,它实际上不是相同的代码,只是相似。

至于装箱,对于float/double类型,JIT编译器可以内联您的lambda,看到装箱的值不会转义并消除装箱。例如,让我们考虑这样的方法:

代码语言:javascript
复制
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汇编程序的启动方式如下:

代码语言:javascript
复制
# {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调用和装箱被展开并简化为单个vaddsd指令。没有调用,没有装箱,没有内存访问,只有两个xmm寄存器。真快啊。

不幸的是,对于Integer/Long来说,由于缓存某些值,产生的汇编程序就不那么干净了。JIT编译器不能仅仅用简单的add替换它,因为它并不确切地知道,例如,对于缓存的Integer.valueOf(10),您实际上将得到原始的10值。谁知道呢,例如,您可能使用反射替换了一些值,使之成为2+2=5。然而,没有创建新的对象:如果缓存中缺少参数,那么实际上不会调用new Integer

票数 2
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/32640621

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档