Scala在通用特征方法中找不到案例类参数
Posted
技术标签:
【中文标题】Scala在通用特征方法中找不到案例类参数【英文标题】:Scala can't find case class parameter in generic trait method 【发布时间】:2021-07-05 09:37:48 【问题描述】:我有以下用通用方法实现特征的案例类。目标是让每个扩展 trait 的类 S
以这样的方式实现方法,即该方法使用 S
类型的参数并返回 S
类型的值。
case class Foo(x: Int) extends Bar
def baz[Foo](v: Foo): Foo =
Foo(v.x + x)
trait Bar
def baz[S <: Bar](v: S): S
但是,在编译这个时,我得到以下错误,x
找不到。
error: value x is not a member of type parameter Foo
Foo(v.x + x)
^
one error found
对我来说更奇怪的是,当我将方法更改为返回 Foo(3)
(从而删除对 x
的访问)时,我收到以下错误:
error: type mismatch;
found : <empty>.Foo
required: Foo(in method baz)
Foo(3)
^
one error found
我不确定<empty>.Foo
和Foo(in method baz)
是什么类型,以及为什么这两个类型不只是Foo
。显然,我对 Scala 的类型系统有一些误解,因此不胜感激。
编辑
我希望有多个类实现Bar
。我希望能够在 Bar
类型的 val 上调用 baz
。
考虑在下面的示例中,a
和 b
是从其他地方获得的,根据我的程序逻辑,我知道它们都是同一类的实例(不一定是 Foo
)。在这种情况下,有什么方法可以合法拨打baz
吗?我知道我可以将两者都转换为Foo
,但这发生在我不知道特定类是什么的情况下。也就是说,我知道a
和b
都是T <: Bar
类型的T
,但我不知道T
是什么。
object Foo
def main(args: Array[String]): Unit =
val a: Bar[_] = Foo(3)
val b: Bar[_] = Foo(2)
a.baz(b) // This call is invalid
【问题讨论】:
顺便说一句,this answer 对您较早的问题提出的建议与我所做的大致相同,并且您接受了它。这对你不起作用吗? @OriginalOriginalOriginalVI 我想我没有看到这两者之间的联系,但我认为你是对的,它们基本上是同一件事。也许这应该只是作为副本关闭。 我建议在这种情况下使用类型类(尽管它们不适用于您的示例,因为您在那里使用了通配符)。如果它不能解决您的问题,我也建议不接受我的答案(我正在编辑它,但在那之前其他人可能会回答) 您的示例似乎太复杂了。我会选择 F[_],它会更简单地解决您的问题 @user2963757 我不明白你的建议。 【参考方案1】:baz
的类型参数不像你想象的那样工作。在原始特征 Bar
中,它告诉您可以传入 any S
,扩展 Bar
和 baz
将输出另一个 S
。这意味着在Foo
中,您不仅要处理Foo
,还要处理继承自Bar
的其他类。
此外,类型参数Foo
隐藏类Foo
。相当于这样:
case class Foo(x: Int) extends Bar
def baz[T](v: T): T =
Foo(v.x + 1)
T
与Foo
和Bar
完全无关,所以你的方法也不满足baz
的签名,因为它应该是T <: Bar
。
我认为您想要做的是通过将类型参数赋予 Bar
本身来获得 S
(这称为 F 有界多态性):
case class Foo(x: Int) extends Bar[Foo]
def baz(v: Foo): Foo =
Foo(v.x + 1)
trait Bar[S <: Bar[S]] this: S =>
def baz(v: S): S
这里S
基本上可以用来指代扩展Bar
的类的类型,所以编译器知道baz
输入和输出Foo
s。
你以后可以像这样使用它:
def test[T <: Bar[T]](a: T, b: T) = a.baz(b)
不过,根据您的编辑,您似乎想要一个类型类。我建议将Bar
的定义更改为:
trait Bar[T]
def baz(t: T): T
那么Foo
可以这样定义:
case class Foo(x: Int)
Foo
的 Bar
的隐式实例将单独提供(可能在 Bar
的伴随对象中):
object Bar
implicit val fooBar: Bar[Foo] = new Bar
def baz(v: Foo): Foo = Foo(v.x + 1)
//For convenience
def apply[T](implicit bar: Bar[T]): bar
你以后可以像这样使用它:
def test[T : Bar](a: T, b: T) = Baz[T].bar(a, b)
请参阅Advantages of F-bounded polymorphism over typeclass for return-current-type problem 了解更多信息。
【讨论】:
第二个给我trait Bar takes type parameters
@GuruStron 我的错,让我修复它。
鉴于类型参数现在与类相关联,我不知道如何使用我知道的两个 Bar
类型的 val 调用 baz
实际上是同一类的实例,而无需将两者都转换为特定类(例如Foo
)。
@MichaelMior 我不明白。您是否想要让Foo
处理Bar
的任何子类型S
?如果是这样,我可以更改答案,但您能否编辑您的问题以更好地解释您的意思?
@OriginalOriginalOriginalVI 感谢您的耐心等待。我编辑了我的答案以试图澄清。以上是关于Scala在通用特征方法中找不到案例类参数的主要内容,如果未能解决你的问题,请参考以下文章
无法在Postgres中实例化外部Hive Metastore /在类路径中找不到驱动程序