代码之家  ›  专栏  ›  技术社区  ›  Majid Azimi

当参数限制为scala中的anyRef和anyVal时发生冲突的方法

  •  1
  • Majid Azimi  · 技术社区  · 6 年前

    scala编译器检测到以下两个 map 作为重复项的函数相互冲突:

    class ADT {
        def map[Output <: AnyVal](f: Int => Output): List[Output] = ???
        def map[Output >: Null <: AnyRef](f: Int => Output): List[Output] = ???
    }
    

    的类类型 Output 参数不同。第一个限制为 AnyVal 第二个限制为 AnyRef .如何区分它们?

    3 回复  |  直到 6 年前
        1
  •  4
  •   Dima    6 年前

    问题不在于区分 AnyVal AnyRef 就像绕过了这样一个事实,即删除后两个方法签名都变为相同的。

    这里有一个巧妙的方法来解决这种问题。它类似于@som snytt所做的,但更通用一些,因为它也适用于其他类似的情况(例如 def foo(f: Int => String): String = ??? ; def foo(f: String => Int): Int = ??? 等等):

    class ADT {
      def map[Output <: AnyVal](f: Int => Output): List[Output] = ???
      def map[Output >: Null <: AnyRef](f: Int => Output)(implicit dummy: DummyImplicit): List[Output] = ???
    }
    

    最可爱的是,这是“开箱即用”的。显然,A DummyImplicit 是标准库的一部分,并且您始终拥有范围内的内容。 通过这种方式,您也可以有两个以上的重载,只需向列表中添加更多的虚拟对象即可。

        2
  •  2
  •   som-snytt    6 年前
    scala 2.13.0-M5> :pa
    // Entering paste mode (ctrl-D to finish)
    
    object X {
        def map[Output <: AnyVal](f: Int => Output) = 1
        def map[O](f: Int => O)(implicit ev: O <:< AnyRef) = 2
    }
    
    // Exiting paste mode, now interpreting.
    
    defined object X
    
    scala 2.13.0-M5> X.map((x: Int) => x*2)
    res0: Int = 1
    
    scala 2.13.0-M5> X.map((x: Int) => "")
    res1: Int = 2
    
        3
  •  2
  •   mdm    6 年前

    你可以用一个typeclass map 方法。

    使用您的确切示例:

    trait MyTC[Output]{
      def map(f: Int => Output): List[Output]
    }
    
    object MyTC{
      def apply[A](a : A)(implicit ev : MyTC[A]) : MyTC[A] = ev 
    
      implicit def anyRefMyTc[A <: AnyRef] : MyTC[A]  = new MyTC[A]{
        def map(f: Int => A): List[A] = { println("inside sub-AnyRef"); List.empty }
      }
      implicit def anyValMyTc[A <: AnyVal] : MyTC[A] = new MyTC[A]{
        def map(f: Int => A): List[A] = { println("inside sub-AnyVal"); List.empty }
      }
    }
    
    import MyTC._
    
    val r1 = Option("Test1")
    val r2 = List(5)
    val v1 = true
    val v2 = 6L
    
    // The functions here are just to prove the point, and don't do anything.
    MyTC(r1).map(_ => None)
    MyTC(r2).map(_ => List.empty)
    MyTC(v1).map(_ => false)
    MyTC(v2).map(_ => 10L)
    

    这将打印:

    SUB ANYREF内部
    SUB ANYREF内部
    在Sub AnyVal内
    在Sub AnyVal内

    这种方法的优点是,如果你随后选择进一步专门化某些特定类型的行为(例如,说你想做一些特定的事情 Option[String] )您可以很容易地做到:

    // This is added to MyTC object 
    implicit val optMyTc : MyTC[Option[String]] = new MyTC[Option[String]]{
        def map(f: Int => Option[String]): List[Option[String]] = { println("inside Option[String]"); List.empty }
      }
    

    然后,重新运行代码将打印:

    内部选项[字符串]
    SUB ANYREF内部
    在Sub AnyVal内
    在Sub AnyVal内