【问题标题】:Restrict function generic type parameters限制函数泛型类型参数
【发布时间】:2021-06-27 21:58:06
【问题描述】:

结构如下:

trait ParentType
trait ChildType extends ParentType

case class FooBar[+T <: ParentType](s: String, data: T)
object FooBar {
  def apply[T <: ChildType](i: Int, data: T): FooBar[T] = new FooBar(s"$i", data)
}
case class Foo(a: Int) extends ParentType
case class Bar(s: String) extends ChildType


def function1[T <: ParentType](obj: FooBar[T]): Unit = ()
def function2[T <: ChildType](obj: FooBar[T]): Unit = ()

是否可以(以及以何种方式)更改function1function2 的签名(带有泛型声明),使得function1 只能排除FooBar 类型为T 的参数,即直接后代的ParentType(并禁止任何T 的直接后代ChildType)和function2 以另一种方式 - 除了只有T 的参数是ChildType 的直接后代并禁止所有ParentType T

换句话说,这不应该编译

function1(FooBar("a", Bar("1"))) //because Bar is not direct descendant of ParentType

【问题讨论】:

  • 你为什么要这么复杂和奇怪的限制类型?您的要求甚至直接违反了 Liskov 原则,这让我认为这是一个 XY 问题,并且您使用继承的正确解决方案是一个失败的抽象。 - 如果你能解释你的元问题,我们可能会提供更好的选择。
  • 假设 ParentType 是 Animal 而 ChildType 是 Mammal。我希望function2 只接受哺乳动物的“容器”FooBar,而function1 - 所有其他组类型的动物(但不是哺乳动物):FishReptile 等。希望现在更清楚了。谢谢。
  • 你可以为def function1[T](data: FooBar[T])(implicit ev: T &lt;:&lt; ChildType)function2 你可能宁愿重载function1 这样如果没有找到证据,就会选择重载(不确定这是否可行) i> 或从 Shapeless 中拉出=!:=
  • @LuisMiguelMejíaSuárez 谢谢,使用 shapless 解决了我的问题

标签: scala generics


【解决方案1】:

这个问题恐怕没有多大意义,但要回答你在cmets中写的内容:

我希望 function2 仅接受哺乳动物的“容器”FooBars

所以,应该是def function2(m: Foobar[Mammal])

和功能1 - 所有其他组类型的动物(但不是哺乳动物)

这是一个奇怪的要求。如果你确定你需要这个(我真的非常怀疑你真的需要),我建议这样:

   trait NotMammal extends Animal
   class Fish extends NotMammal
   class Reptile extends NotMammal

   ... 

   def funcction1(n: Foobar[NotMammal]) = ???

【讨论】:

  • 所以你基本上是在建议为“那些不是哺乳动物的人”制作一些标记类型?关于函数,如果我还需要函数应该有一个为T 绑定的上下文(假设来自scalacheck 的Arbitrary,并且我想将该函数与scalacheck-shapless 一起使用来为每种特定类型生成arbitraries,它会起作用吗? :Monkey 哺乳动物,Salmon 鱼类等)?
  • @maks 抱歉,我不知道那些东西是什么......或者你想要达到的目标。
【解决方案2】:

在@LuisMiguelMejíaSuárez 的帮助下(请参阅他对问题的 cmets),我设法实现了我想要的。因此,考虑到我需要 scalacheck 的 Arbitrary 上下文绑定在这些函数声明中,Luis 建议使用 shapless(特别是我发现 &lt;:!&lt; 可以解决我的问题)帮助

    import shapless._

    def function1[T <: ParentType](obj: FooBar[T])(implicit: arb: Arbitrary[T], ev: T <:!< ChildType): Unit = ()
    def function2[T <: ChildType : Arbitrary](obj: FooBar[T]): Unit = ()

不幸的是,我没有设法实现对function1 的覆盖(只是使用不同的ev 类型),但是有了这些具有不同名称的函数,我可以从function2 重用function1(我只需要调用function1 带有 &lt;:!&lt; 实例的显式参数,即 shapeless.nsub)。

因此,使用此解决方案,任何ChildType 实例都不能应用于function1(至少按原样),因为这样的应用程序会导致

ambiguous implicit values:
 both method nsubAmbig1 in package shapeless of type [A, B >: A]A <:!< B
 and method nsubAmbig2 in package shapeless of type [A, B >: A]A <:!< B
 match expected type Bar <:!< ChildType

我猜这就是使用 &lt;:!&lt; 类型背后的设计(但如果明确指定 &lt;:!&lt; 的实例,它将起作用)

这是一个 link 给 scastie 的工作表来尝试问题

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-11-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多