扩展类时类型的Scala冲突



我已经定义了一个抽象基类,如以下内容:

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),其中

  1. o1, o2是类型Derived
  2. 的对象
  3. 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值的函数将愉快地接受这些功能;毕竟,DerivedBase。基本上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)

相关内容

  • 没有找到相关文章

最新更新