我有两个具有不同类型签名的函数,我想将它们存储在Map中:
val add = ((a: Int, b: Int) => a + b)
val odd = ((a: Int) => a % 2 == 0)
var funcs = Map[String, Any]("add" -> add, "odd" -> odd)
然后我想在以后访问这些功能,而不必知道他们的类型。我该怎么做?我可以写:
funcs("add").asInstanceOf[(Int, Int) => Int](1, 2)
但这需要我
有什么方法可以找出存储为Any
的对象类型并将其转换为该类型?
答案 0 :(得分:6)
虽然这当然不是一个好主意,如果你真的必须这样做,你可以使用反射来做。
使用PaulP的Invocation实用程序:
scala> val add: (Int, Int) => Int = _ + _
add: (Int, Int) => Int = <function2>
scala> val isOdd: Int => Boolean = _ % 2 != 0
isOdd: Int => Boolean = <function1>
scala> import Invocation._
import Invocation._
scala> val funcs = Map("add" -> add, "isOdd" -> isOdd)
funcs: scala.collection.immutable.Map[java.lang.String,ScalaObject] = Map(add -> <function2>, isOdd -> <function1>)
scala> funcs("add") o 'apply(3, 4)
res18: Any = 7
scala> funcs("isOdd") o 'apply(11)
res19: Any = true
答案 1 :(得分:4)
有什么方法可以找出存储为Any的对象类型并将其转换为该类型?
这实际上没有意义。让我们假设我能做到,它看起来像这样:
val func = funcs(func_name).toAppropriateType
现在是什么?
func
现在拥有某种未知类型的值。我可以称它为...但我不知道它的签名,那么我通过什么参数呢?并且每次调用时类型/签名都会有所不同(取决于从地图中拉出的内容),因此调用它的代码对于每次调用都必须是不同的;我需要某种方法为func
的每种可能性执行不同的代码...但这将是模式匹配!它必须考虑到所有可能的类型,就像在Any
上匹配一样。
事实上,神奇的toAppropriateType
方法,即使它可以存在,也会让我在使用Any
之上 。我仍然可以使用func
做的唯一事情就是你可以用所有可能的类型(即很少)的所有值来做。
一般情况下,当您从集合中取出某些内容时,您必须使用对该类型允许放入集合的任何内容有效的代码来处理它。唯一的选择是使用诸如asInstanceOf
之类的功能,它会抛弃类型系统的保证,并负责确保您不使用错误的类型;这意味着你必须有其他方式来了解类型系统之外的预期类型。
如果你想拥有一组预先确定的签名函数,那么最好的方法是构造一个只允许这些签名的类型,并让你分辨出它们之间的区别(比如Rex)克尔建议使用Either
)。一旦发现需要在集合中组合不同类型的东西,切换到使用Any
通常会导致比解决的问题更多的问题。
答案 2 :(得分:2)
您可以将它们存储在Either
:
val funcs = Map[String, Either[Int=>Boolean, (Int,Int)=>Int]](
"add" -> Right(add),
"odd" -> Left(odd)
)
现在你可以再次将它们拆开,虽然模式匹配在这里很挑剔(Right(f)
会起作用,但是不要试图在f
上设置条件)。我建议使用fold
,right
,left
(以及isRight
和isLeft
,如果你真的必须),就像这样:
def doSomething(s: String) =
funcs(s).fold(
f => { f(4) }, // Left is Int=>Boolean
g => { g(3,5)==8 } // Right is (Int,Int)=>Int
)
据推测,你想要一些方法来加载适当的参数,而不是我输入的虚拟值。
如果你有很多函数变体,你可以定义自己的类似层次结构,除了更多不同的情况,或者你可以嵌套Either
。