【问题标题】:Scala interop with Java SAM without 2.12 M2 flagScala 与没有 2.12 M2 标志的 Java SAM 互操作
【发布时间】:2015-08-04 00:33:59
【问题描述】:

是否有任何公认的技术可以针对使用 Java @FunctionalInterface / SAM / lambda 表达式的 Java-8 API 编写 Scala 代码?

虽然 Java lambda 表达式互操作在 M2 http://www.scala-lang.org/news/2.12.0-M2 的标志下可用,但我更希望类型类/AnyVal 解决方案可以与 scala.FunctionX 特征一起使用。

不幸的是,scala.FunctionX 扩展了 AnyRef 而不是 Any,因此无法将这些特征使用/混合到隐式 AnyVal 类实现中。

补充说:即使 scala.FunctionX 是全局特征(从 Any 扩展),我也不完全确定我将如何实现我的目标。我的用例是这样的:

在我的一个项目中,我选择提供一个 Java-8 API,它带有 Java Stream 接口和类等功能接口,以迎合尽可能广泛的基于 JVM 的客户端语言的受众,例如闭包,Scala,Kotlin。对于使用我的 Java-8 API 的每种客户端语言,我将编写适当的绑定(如有必要)以使用特定于语言的惯用语,如果在访问 Java-8 API 时感觉在该语言中很笨拙。

顺便说一句。我也会对在 Kotlin-Java 互操作上下文中提出这个问题的任何 cmets 感兴趣。

【问题讨论】:

    标签: java scala jvm java-8


    【解决方案1】:

    这个 Scala 程序演示了我的问题的一方面,即如何让 Scala 函数伪装成 Java 8 Lambda。

    通过创建一些隐式 Scala 函数来将 Scala 函数转换为它们的 Java 8 FunctionalInterface 对应类型,从语法和习惯上来说,这似乎可以正常工作。

    当然,需要注意的是,此方法没有利用 Java 8 通过 invokedynamic 优化 lambda 创建的能力。

    因此,这种方法会导致为 Scala 函数实例创建 JVM 对象,与 Java 8 原生 lambda 相比,这可能会影响内存使用和性能。

    对于硬币的另一面,即如何让 Java 8 Lambda 伪装成 Scala 函数,我想必须编写一些 Java 代码才能与 Scala 互操作(如果目标是拥有 Scala API可以从 Java 调用)。

    贾斯汀·约翰逊, 关于我的发条计划的微博, JVM 上 XPath/XQuery 的新实现, 作为 Twitter 上的@MartianOdyssey

    https://twitter.com/MartianOdyssey

    /**
      * Scala Functions masquerading as Java 8 Lambdas.
      *
      * (C) Justin Johansson 2015.
      *
      * Microblogging about my Project Clockwork, a
      * new implementation of XPath/XQuery on the JVM,
      * as @MartianOdyssey on Twitter (https://twitter.com/MartianOdyssey).
      *
      * Permission to use this code is granted under Apache License,
      * Version 2.0 and providing attribution is afforded to author,
      * Justin Johansson.
      */
    
    package lab
    
    import scala.language.implicitConversions
    
    import java.util.{ Arrays => JArrays, List => JList }
    import java.util.function.{ Consumer => JConsumer, Function => JFunction, Predicate => JPredicate }
    import java.util.stream.{ Stream => JStream }
    
    object JLambda extends App {
      println("JLambda: Scala to Java 8 lambda test")
    
      implicit def func1ToJConsumer[T](func: T => Unit): JConsumer[T] = {
        new JConsumer[T] {
          def accept(arg: T): Unit = func(arg)
        }
      }
    
      implicit def func1ToJFunction[T, R](func: T => R): JFunction[T, R] = {
        new JFunction[T, R] {
          def apply(arg: T): R = func(arg)
        }
      }
    
      implicit def func1ToJPredicate[T](func: T => Boolean): JPredicate[T] = {
        new JPredicate[T] {
          def test(arg: T): Boolean = func(arg)
        }
      }
    
      val myList = JArrays.asList("cake", "banana", "apple", "coffee")
      println(s"myList = $myList")
    
      val myListFiltered: JStream[String] = myList.stream
        .filter { x: String => x.startsWith("c") }
    
      val myListFilteredAndMapped: JStream[String] = myListFiltered
        .map { x: String => x.toUpperCase }
    
      myListFilteredAndMapped.forEach { x: String => println(s"x=$x") }
    }
    
    
    /*
    Outputs:
    JLambda: Scala to Java 8 lambda test
    myList = [cake, banana, apple, coffee]
    x=CAKE
    x=COFFEE
    */
    

    【讨论】:

    • 到目前为止,这种方法对我来说似乎效果很好,因此已经编写了 java.util.function 中的所有 43 个接口变体,并将转换定义放在 Scala-Java 互操作包中。如果有兴趣,很高兴发布(在 Apache 许可下)。
    【解决方案2】:

    顺便说一句。我也会对在 Kotlin-Java 互操作上下文中提出这个问题的任何 cmets 感兴趣。

    Kotlin 的 FunctionX 接口是 SAM 的,所以不需要做任何额外的事情来让 Java 8 理解它们

    【讨论】:

      猜你喜欢
      • 2016-05-17
      • 2018-10-29
      • 1970-01-01
      • 1970-01-01
      • 2013-04-11
      • 1970-01-01
      • 1970-01-01
      • 2014-12-25
      • 1970-01-01
      相关资源
      最近更新 更多