代码之家  ›  专栏  ›  技术社区  ›  hi.nitish

为什么不能为Java中的var关键字分配lambda表达式?

  •  74
  • hi.nitish  · 技术社区  · 8 年前

    允许分配 var 在Java 10中,字符串如下:

    var foo = "boo";
    

    但不允许使用lambda表达式对其进行赋值,例如:

    var predicateVar = apple -> apple.getColor().equals("red");
    

    为什么它不能推断lambda或方法引用类型,而它可以推断其他类型,如 String ,则, ArrayList ,用户类等。?

    7 回复  |  直到 8 年前
        1
  •  68
  •   Brian Goetz    8 年前

    这与 var 。这与lambda是否有 独立类型 。这条路 风险值 工作原理是,它计算RHS上初始值设定项的独立类型,并推断出。

    由于lambda表达式和方法引用是在Java 8中引入的,所以它们没有独立的类型——它们需要 目标类型 ,它必须是功能接口。

    如果您尝试:

    Object o = (String s) -> s.length();
    

    您还将得到一个类型错误,因为编译器不知道要将lambda转换为哪个函数接口。

    要求推断 风险值 只是让事情变得更难,但既然简单的问题无法回答,那么越难的问题也无法回答。

    请注意,您可以通过其他方式(如cast)提供目标类型,然后它就会工作:

    var x = (Predicate<String>) s -> s.isEmpty();
    

    因为现在RHS有一个独立的类型。但是您最好通过提供 x 清单类型。

        2
  •  62
  •   Community Mohan Dere    6 年前

    Local-Variable Type Inference JEP :

    推理过程实质上只是为变量提供其初始值设定项表达式的类型。一些微妙之处:

    • 初始值设定项没有目标类型(因为我们还没有推断出它)。需要此类类型的多边形表达式,如 兰巴斯 、方法引用和数组初始值设定项将触发错误。

    由于lambda表达式本身没有类型,因此无法为 var


    。。。类似地,可以设置默认规则。

    当然,您可以想出一种绕过此限制的方法。为什么开发商决定不这样做,这真的取决于猜测,除非参与决策的人可以在这里回答。(更新:已回答 here .)如果您有兴趣,可以在openjdk邮件列表中询问: http://mail.openjdk.java.net/mailman/listinfo

    如果让我猜的话,他们可能不想把lambda推断与 风险值 到一组特定的功能接口类型,这将排除任何第三方功能接口类型。更好的解决方案是推断泛型函数类型(即。 (Apple) -> boolean )可以转换为兼容的功能接口类型。但是JVM没有这样的函数类型,在创建lambda表达式的项目中已经做出了不实现它们的决定。如果您对具体原因感兴趣,请再次询问开发人员。

        3
  •  33
  •   Karl Bielefeldt    8 年前

    对于所有认为这是不可能的、不需要的或不需要的人,我只想指出,Scala可以通过只指定参数类型来推断lambda的类型:

    val predicateVar = (apple: Apple) => apple.getColor().equals("red")
    

    在哈斯克尔,因为 getColor 将是一个未附加到对象的独立函数,并且由于它执行完整的Hindley-Milner推理,因此您甚至不需要指定参数类型:

    predicateVar = \apple -> getColor apple == "red"
    

    这非常方便,因为对于程序员来说,显式指定并不是简单的类型,而是更复杂的类型。

    换句话说,它不是Java 10中的一个特性。这是对其实现和以前设计选择的限制。

        4
  •  5
  •   Ousmane D.    8 年前

    正如一些人已经提到的,应该是什么类型的 var 推断,为什么要推断?

    声明:

    var predicateVar = apple -> apple.getColor().equals("red");
    

    是不明确的,并且编译器没有合理的理由选择 Function<Apple, Boolean> 结束 Predicate<Apple> 反之亦然,假设 apple lambda中的标识符表示 Apple isntance。

    另一个原因是lambda本身没有 可说出的 类型,因此编译器无法推断它。

    而且 “如果可能的话” 设想一下开销,因为编译器每次将lambda分配给 风险值 变量

        5
  •  5
  •   Alexander Druzhynin    8 年前

    要回答这个问题,我们必须深入了解lambda是什么以及它是如何工作的。

    首先,我们应该了解lambda是什么:

    lambda表达式始终实现一个函数接口,因此当您必须提供如下函数接口时 Runnable ,而不必创建实现该接口的全新类,只需使用lambda语法创建函数接口所需的方法即可。请记住,lambda仍然具有它所实现的功能接口的类型。

    考虑到这一点,让我们更进一步:

    这非常适合Runnable,我可以创建这样的新线程 new Thread(()->{//put code to run here}); 而不是创建一个全新的对象来实现功能接口。这是因为编译器知道 Thread() 获取Runnable类型的对象,因此它知道lambda表达式必须是什么类型。

    然而,在将lambda分配给局部变量的情况下,编译器不知道这个lambda实现的是什么函数接口,因此无法推断出什么类型 var 应该是。因为它可能实现了用户创建的功能界面,或者可能是 runnable 界面,根本没有办法知道。

    这就是为什么lambda不使用var关键字的原因。

        6
  •  2
  •   user9455968 user9455968    8 年前

    因为这是一个非功能:

    这种处理将局限于具有初始值设定项的局部变量、增强for循环中的索引以及在传统for循环中声明的局部变量;它不适用于方法形式、构造函数形式、方法返回类型、字段、catch形式或任何其他类型的变量声明。

    http://openjdk.java.net/jeps/286

        7
  •  0
  •   Starrow Pan    6 年前

    简而言之,var和lambda表达式的类型都需要推断,但方式相反。变量的类型由初始值设定项推断:

    var a = new Apple();
    

    lambda表达式的类型由上下文设置。上下文所期望的类型称为目标类型,通常由声明推断,例如。

    // Variable assignment
    Function<Integer, Integer> l = (n) -> 2 * n;
    // Method argument 
    List<Integer> map(List<Integer> list, Function<Integer, Integer> fn){
        //...
    }
    map(List.of(1, 2, 3), (n) -> 2 * n);
    // Method return 
    Function<Integer, Integer> foo(boolean flag){
        //...
        return (n) -> 2 * n;
    }
    

    因此,当var和lambda表达式一起使用时,前者的类型需要由后者推断,而后者的类型需要由前者推断。

    var a = (n) -> 2 * n;
    

    这种困境的根源是Java无法唯一地决定lambda表达式的类型,这进一步是由Java的名义类型系统而非结构类型系统造成的。也就是说,结构相同但名称不同的两种类型不被视为相同,例如。

    class A{
        public int count;
        int value(){
            return count;
        }
    }
    
    class B{
        public int count;
        int value(){
            return count;
        }
    }
    
    Function<Integer, Boolean>
    Predicate<Integer>