我已经定义了一个抽象基类,如以下内容:
abstract class Base() {
val somevariables
}
然后,我像以下内容一样扩展了此类:
case class Derived (a: SomeOtherClass, i: Int) extends Base {
//Do something with a
}
然后,我有一种方法(独立于类(,如下:
def myMethod (v1: Base, v2: Base, f:(Base, Base) => Int ): Int
我想将上述方法用作myMethod(o1, o2, f1)
,其中
-
o1, o2
是类型Derived
的对象 -
f1
如下def f1(v1: Derived, v2: Derived): Int
现在,这给了我一个错误,因为myMethod
期望该功能f1
为(Base, Base) => Int
,而不是(Derived, Derived) => Int
。但是,如果我将f1
的定义更改为(Base, Base) => Int
,则会给我一个错误,因为内部我想使用SomeOtherClass
中的一些变量,这是Base
没有的参数。
您应该使用类型参数来确保 myMethod
中的类型正确排列。
def myMethod[B <: Base](v1: B, v2: B)(f: (B, B) => Int): Int
或更一般的:
def myMethod[B <: Base, A >: B](v1: B, v2: B)(f: (A, A) => Int): Int
如果您想能够使用函数 f1 其中function f2 的期望是 f1 必须要么是相同类型的(输入参数和返回值(,要么是 f2 的子类。Liskov替代原则告诉我们,要使一个函数成为另一个功能,它需要少(或相同(并提供更多(或相同(。
因此,如果您有一种方法,即作为参数采用类型(Fruit, Fruit) => Fruit
的函数,这里是某些有效功能的类型,您可以将其传递给该方法:
- (水果,水果(=>水果
- (水果,水果(=>苹果
- (任何,任何(=>水果
- (任何,任何(=>苹果
这与协方差/违反规则有关;例如,Scala中的每个单参数功能都是具有两个类型参数Function2[-S, +T]
的特征。您可以看到它在其参数类型中是违反的,而在返回类型中的协变量 - 需要S
或更小("少",因为它更一般,因此我们丢失了信息(,并提供T
或更多("更多"(因为它更具体,更具体,更具体,因此,我们获得了更多信息(。
这使我们解决了您的问题。如果您以相反的方式有事,试图在预期(Derived, Derived) => Int
的地方安装(Base, Base) => Int
,那将起作用。方法myMethod
显然期望用类型Derived
的值馈送此功能,并且采用类型Base
值的函数将愉快地接受这些功能;毕竟,Derived
是Base
。基本上myMethod
在说的是:"我需要一个可以处理Derived
S的函数",并且任何知道如何与Base
S一起使用的功能也可以采用其任何子类,包括Derived
。
其他人指出,您可以将函数类型f
的参数设置为Base
的子类型,但是在某个时候,您可能希望将V1和V2与该函数一起使用,然后您需要恢复通过模式匹配来降低。如果您对此感到满意,那么您也可以直接在功能上进行模式匹配,试图弄清楚它的真实本质。无论哪种方式,在这种情况下,图案匹配很烂,因为每次引入新类型时,您都需要围绕myMethod
进行小提琴。
这是您可以通过类型类更优雅地解决它的方法:
trait Base[T] {
def f(t1: T, t2: T): Int
}
case class Shape()
case class Derived()
object Base {
implicit val BaseDerived = new Base[Derived] {
def f(s1: Derived, s2: Derived): Int = ??? // some calculation
}
implicit val BaseShape = new Base[Shape] {
def f(s1: Shape, s2: Shape): Int = ??? // some calculation
}
// implementations for other types
}
def myMethod[T: Base](v1: T, v2: T): Int = {
// some logic
// now let's use f(), without knowing what T is:
implicitly[Base[T]].f
// some other stuff
}
myMethod(Shape(), Shape())
这里发生的是 myMethod
说:"我需要两个类型T的值,我需要在范围中有一个隐式Base[T]
(这是[T: Base]
部分,这是一种奇特的方式,说明您需要一个隐式参数Base[T]
类型;这样,您将以其名称访问它,然后通过implicitly
访问它。然后,我知道我将拥有执行所需逻辑的f()
。而且,由于逻辑可以基于类型具有不同的实现,因此这是临时多态性和类型类的情况,是处理该类型的好方法。
这里很酷的是,当引入具有自己的f
实现的新类型时,您只需要将此实现放在Base
Companion对象中作为隐式值,以便myMethod
可用。方法myMethod
本身保持不变。
根据我的(非常简单的(测试,此更改...
def myMethod[B <: Base](v1: Base, v2: Base, f:(B, B) => Int ): Int = ???
...将允许这两种方法...
def f1(a: Derived, b:Derived): Int = ???
def f2(a: Base, b:Base): Int = ???
...被接受为传递的参数。
myMethod(Derived(x,1), Derived(x,2), f1)
myMethod(Derived(x,1), Derived(x,2), f2)