加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 综合聚焦 > 服务器 > 安全 > 正文

scala – 类型成员的上下文边界或如何在成员实例化之前推迟隐式

发布时间:2020-12-16 18:34:22 所属栏目:安全 来源:网络整理
导读:在下面的示例中,有没有办法避免隐式解析选择defaultInstance并使用intInstance?代码后的更多背景: // the following part is an external fixed APItrait TypeCls[A] { def foo: String}object TypeCls { def foo[A](implicit x: TypeCls[A]) = x.foo impl
在下面的示例中,有没有办法避免隐式解析选择defaultInstance并使用intInstance?代码后的更多背景:

// the following part is an external fixed API

trait TypeCls[A] {
  def foo: String
}

object TypeCls {
  def foo[A](implicit x: TypeCls[A]) = x.foo

  implicit def defaultInstance[A]: TypeCls[A] = new TypeCls[A] {
    def foo = "default"
  }

  implicit val intInstance: TypeCls[Int] = new TypeCls[Int] {
    def foo = "integer"
  }
}

trait FooM {
  type A
  def foo: String = implicitly[TypeCls[A]].foo
}

// end of external fixed API

class FooP[A:TypeCls] { // with type params,we can use context bound
  def foo: String = implicitly[TypeCls[A]].foo
}

class MyFooP extends FooP[Int]

class MyFooM extends FooM { type A = Int }

object Main extends App {

  println(s"With type parameter: ${(new MyFooP).foo}")
  println(s"With type member:    ${(new MyFooM).foo}")
}

实际产量:

With type parameter: integer
With type member:    default

期望的输出:

With type parameter: integer
With type member:    integer

我正在使用第三方库,该库使用上述方案为类型类TypeCls提供“默认”实例.我认为上面的代码是一个演示我的问题的最小例子.

用户应该混合FooM特性并实例化抽象类型成员A.问题是由于defaultInstance调用(new MyFooM).foo不解析专用的intInstance而是提交到defaultInstance,这不是我的意思想.

我添加了一个使用类型参数的替代版本,称为FooP(P = Parameter,M = Member),它避免通过使用type参数上的上下文来解析defaultInstance.

对于类型成员,是否有相同的方法来执行此操作?

编辑:我的简化中有一个错误,实际上foo不是def而是val,因此无法添加隐式参数.因此目前的答案都不适用.

trait FooM {
  type A
  val foo: String = implicitly[TypeCls[A]].foo
}

// end of external fixed API

class FooP[A:TypeCls] { // with type params,we can use context bound
  val foo: String = implicitly[TypeCls[A]].foo
}

解决方法

在这个特定情况下最简单的解决方案是foo本身需要一个TypeCls [A]的隐式实例.
唯一的缺点是它会在每次调用foo时传递,而不是在实例化时传递
FooM.所以你必须确保它们在每次调用foo时都在范围内.虽然只要TypeCls实例在伴随对象中,您就没有任何特殊要做.

trait FooM {
  type A
  def foo(implicit e: TypeCls[A]): String = e.foo
}

更新:在上面的回答中,我设法错过了FooM无法修改的事实.此外,该问题的最新编辑提到FooM.foo实际上是一个val而不是def.

好消息是你正在使用的API被破坏了. FooM.foo wille无法返回任何有用的东西(无论A的实际值如何,它都会将TypeCls [A]解析为TypeCls.defaultInstance).唯一的出路是在已知A的实际值的派生类中覆盖foo,以便能够使用TypeCls的正确实例.幸运的是,这个想法可以与您使用具有上下文绑定的类(在您的情况下为FooP)的原始解决方法相结合:

class FooMEx[T:TypeCls] extends FooM {
  type A = T
  override val foo: String = implicitly[TypeCls[A]].foo
}

现在不要让你的类直接扩展FooM,而是让它们扩展FooMEx:

class MyFoo extends FooMEx[Int]

FooMEx与原始FooP类之间的唯一区别是FooMEx确实扩展了FooM,因此MyFoo是FooM的正确实例,因此可以与固定API一起使用.

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读