我在Scala中有一个具有单一方法的特征。称之为Computable,单个方法是compute(input:Int):Int。我无法弄清楚我是否应该
支持它成为特质的一个因素是我可以有用地添加一些额外的方法。但当然,如果它们都是根据计算方法实现的,那么我可以将它们分解为一个单独的对象。
支持仅使用函数类型的一个因素是简单性以及匿名函数的语法比匿名Computable实例的语法更简洁。但是,我无法区分实际上是Computable实例的对象与将Int映射到Int但不打算在与Computable相同的上下文中使用的其他函数。
其他人如何处理此类问题?这里没有对错的答案;我只是在寻求建议。
答案 0 :(得分:8)
如果你让它成为Trait并且仍然希望能够使用轻量级函数语法,你还可以在你想要它们的地方另外添加一个隐式转换:
scala> trait Computable extends (Int => Int)
defined trait Computable
scala> def computes(c: Computable) = c(5)
computes: (c: Computable)Int
scala> implicit def toComputable(f: Int => Int) = new Computable { def apply(i: Int) = f(i) }
toComputable: (f: (Int) => Int)java.lang.Object with Computable
scala> computes( (i: Int) => i * 2 )
res0: Int = 10
答案 1 :(得分:3)
创建从函数类型扩展的特征可能有用,原因有两个。
您的函数对象执行特殊且非显而易见(并且难以键入)的操作,并且您可以参数化构造函数中的轻微变体。例如,假设您正在编写特征以在XML树上执行XPath查询。 apply函数将隐藏构造XPath查询机制的几种工作,但仍然值得实现Function1
接口,以便您可以使用map
或{从一大堆不同的节点开始查询{1}}。
作为#1的扩展,您希望在构造时进行一些处理(例如,解析XPath表达式并将其编译为快速运行),您可以在对象的构造函数中提前执行一次(如果你只是在没有子类化的情况下调用flatMap
,那么编译只能在运行时进行,因此每次查询都会重复编译。)
您希望将加密函数(类型为Function
)作为隐式但不是全部Function1[String,String]
执行加密。通过派生Function1[String,String]
并命名子类/特征Function1[String,String]
,您可以确保只隐式传递正确子类的函数。 (声明EncryptionFunction
时不是这样。)
我希望这很清楚。
答案 2 :(得分:1)
听起来您可能想要使用structural type。它们也被称为隐式接口。
然后,您可以重构当前接受Computable
的方法,以接受任何具有compute(input: Int)
方法的内容。
答案 3 :(得分:1)
一个选项是定义一个类型(你仍然可以称之为Computable),目前是Int => Int。每当您需要可计算的东西时使用它。您将获得从Function1继承的所有好处。然后,如果您意识到需要更多方法,则可以将类型更改为其他特征。
起初:
type Computable = Int => Int
后来:
type Computable = ComputableTrait // with its own methods.
它的一个缺点是你定义的类型实际上不是新类型,更像是一种别名。因此,在将其更改为特征之前,编译器仍将接受其他Int => Int功能。至少,您(开发人员)可以区分。当您更改为特征(并且差异变得很重要)时,编译器将找出何时需要Computable但具有Int =>中间体
如果您希望编译器拒绝其他Int => Int -s从第一天开始,然后我建议使用特征,但扩展Int =>诠释。当你需要调用它时,你仍然可以使用更方便的语法。
另一种选择可能是使用一个接受Int =>的apply方法的trait和一个伴随对象。 Int并创建一个Computable。 然后创建新的Computables几乎就像编写简单的匿名函数一样简单,但是你仍然会进行类型检查(你会通过隐式转换松开)。此外,您可以毫无问题地混合特征(但随后无法按原样使用伴随对象的应用)。