让我们考虑一个具体的例子。我有很多函数采用可变数量的参数,并返回Seq[T]
。说:
def nonNeg(start: Int, count: Int): Seq[Int] =
Iterator.from(start).take(count).toSeq
对于这些功能中的每一个,我需要创建一个" Java版本"该函数返回java.util.List[T]
。我可以创建" Java版本"以上功能:
def javaNonNeg(start: Int, count: Int): java.util.List[Int] =
nonNeg(start, count).asJava
这有点冗长,因为参数列表重复两次。相反,我想创建一个更高级别的函数,它将nonNeg
形式的函数作为参数(任何数量和类型的参数,返回Seq[T]
)并返回一个函数它采用相同的参数,但返回java.util.List[T]
。假设该函数被称为makeJava
,那么我就可以写:
def javaNonNeg = makeJava(nonNeg)
可以使用Shapeless能力abstracting over arity来编写makeJava
吗?如果可以,怎么做,不是,为什么以及如何做到这一点?
答案 0 :(得分:8)
可以使用Shapeless来避免样板 - 你只需要使用普通的旧eta扩展将原始方法转换为FunctionN
,然后转换为采用单个HList
参数的函数,然后返回到具有新结果类型的FunctionN
:
import java.util.{ List => JList }
import shapeless._, ops.function._
import scala.collection.JavaConverters._
def makeJava[F, A, L, S, R](f: F)(implicit
ftp: FnToProduct.Aux[F, L => S],
ev: S <:< Seq[R],
ffp: FnFromProduct[L => JList[R]]
) = ffp(l => ev(ftp(f)(l)).asJava)
然后:
scala> def nonNeg(start: Int, count: Int): Seq[Int] =
| Iterator.from(start).take(count).toSeq
nonNeg: (start: Int, count: Int)Seq[Int]
scala> val javaNonNeg = makeJava(nonNeg _)
javaNonNeg: (Int, Int) => java.util.List[Int] = <function2>
scala> javaNonNeg(1, 4)
res0: java.util.List[Int] = [1, 2, 3, 4]
javaNonNeg
是Function2
,因此从Java可以使用javaNonNeg.apply(1, 4)
。
答案 1 :(得分:3)
对于2个或更多(在下面的代码中为4个)参数,您可以使用隐式参数功能,通过输入参数类型解析结果类型
sealed trait FuncRes[F] {
type Param
type Result
def func : F => Param => Result
}
class Func[T, R](fn : T => R) {
trait FR[F, P] extends FuncRes[F] { type Param = P; type Result = R }
implicit def func2[T1,T2] = new FR[(T1,T2) => T, (T1,T2)] {
def func = f => p => fn(f.tupled(p))
}
implicit def func3[T1,T2,T3] = new FR[(T1,T2,T3) => T, (T1,T2,T3)] {
def func = f => p => fn(f.tupled(p))
}
implicit def func4[T1,T2,T3,T4] = new FR[(T1,T2,T3,T4) => T, (T1,T2,T3,T4)] {
def func = f => p => fn(f.tupled(p))
}
def makeFunc[F](f : F)(implicit ev : FuncRes[F]): ev.Param => ev.Result =
ev.func(f)
}
并在def javaNonNeg = makeJava(nonNeg)
函数看起来像:
object asJavaFunc extends Func((_ : Seq[Int]).asJava)
import asJavaFunc._
def javaNonNeq = makeFunc(nonNeg _)
当然它有一些缺点,但通常它可以满足您的需求。