代码之家  ›  专栏  ›  技术社区  ›  Greg

在Scala中,如何避免强制转换函数参数?

  •  0
  • Greg  · 技术社区  · 7 年前

    我想要各种各样的 “口味” “电线”

    请注意,在使用时,我需要转换参数 "Heavy" 到 thing.WIRE FantasticThing.apply

    我如何避免演员阵容,让Scala意识到这一点 read() 争论是一种争论 基于 StringFlavor 被选中?

    trait Flavor {
      type WIRE
      def read[T](wire: WIRE)(implicit tt: TypeTag[T]): T
    }
    
    trait Maker {
      def make(): Flavor
    }
    
    object StringFlavor extends Maker {
      def make(): Flavor { type WIRE = String } = StringFlavor()
    }
    
    case class StringFlavor() extends Flavor {
      type WIRE = String
      def read[T](wire: String)(implicit tt: TypeTag[T]): T = {
        println(tt.tpe)
        if(tt.tpe =:= typeOf[Int]) {
          5.asInstanceOf[T]
        } else
          throw new Exception("Boom")
      }
    }
    
    object FantasticThing {
      def apply[WIRE](maker: Maker = StringFlavor): Flavor = maker.make()
    }
    
    object RunMe extends App {
      val thing: Flavor = FantasticThing(StringMaker)
      println(thing.read[Int]("Heavy".asInstanceOf[thing.WIRE])) // <-- How can I avoid this cast?
    }
    
    • 根据Luis Miguel的注释进行编辑:我无法将该类型添加到FantasticThing.apply()中,否则我将失去可插入性。我希望用户能够轻松地选择他们想要的口味。我已经重构了一点,用一个工厂模式展示了这一点,它确实添加了您建议的类型信息,但不幸的是,仍然让我需要强制转换顶级。

    如果我提供了一系列的口味,那么用户应该能够做到:

    val foo = FantasticThing(ByteArrayFlavor)
    
    2 回复  |  直到 7 年前
        1
  •  1
  •   Luis Miguel Mejía Suárez    7 年前

    (据我所知) 类型类 和 而不是 类型成员 .

    import scala.reflect.runtime.universe.{TypeTag, typeOf}
    
    implicit class Json(val underlying: String) extends AnyVal
    implicit class Csv(val underlying: String) extends AnyVal
    
    trait Flavor[W] {
      def read[T](wire: W)(implicit tt: TypeTag[T]): T
    }
    
    trait Maker[W] {
      def make(): Flavor[W]
    }
    
    object Maker {
      implicit val StringFlavorMaker: Maker[String] = new Maker[String] {
        override def make(): Flavor[String] = StringFlavor
      }
    
      implicit val JsonFlavorMaker: Maker[Json] = new Maker[Json] {
        override def make(): Flavor[Json] = JsonFlavor
      }
    
      implicit val CsvFlavorMaker: Maker[Csv] = new Maker[Csv] {
        override def make(): Flavor[Csv] = CsvFlavor
      }
    }
    
    case object StringFlavor extends Flavor[String] {
      override final def read[T](wire: String)(implicit tt: TypeTag[T]): T = {
        if(tt.tpe =:= typeOf[Int])
          0.asInstanceOf[T]
        else
          throw new Exception("Boom 1")
      }
    }
    
    case object JsonFlavor extends Flavor[Json] {
      override final def read[T](wire: Json)(implicit tt: TypeTag[T]): T = {
        if(tt.tpe =:= typeOf[Int])
          3.asInstanceOf[T]
        else
          throw new Exception("Boom 2")
      }
    }
    
    case object CsvFlavor extends Flavor[Csv] {
      override final def read[T](wire: Csv)(implicit tt: TypeTag[T]): T = {
        if(tt.tpe =:= typeOf[Int])
          5.asInstanceOf[T]
        else
          throw new Exception("Boom 3")
      }
    } 
    
    object FantasticThing {
      def apply[W](implicit maker: Maker[W]): Flavor[W] = maker.make()
    }
    

    (假设范围中有一个隐式生成器)

    val stringFlavor = FantasticThing[String]
    // stringFlavor: Flavor[String] = StringFlavor
    
    stringFlavor.read[Int]("Heavy")
    // res0: Int = 0
    
    val jsonFlavor = FantasticThing[Json]
    // jsonFlavor: Flavor[Json] = JsonFlavor
    
    jsonFlavor.read[Int]("{'heavy':'true'}")
    // res1: Int = 3
    
    val csvFlavor = FantasticThing[Csv]
    // csvFlavor: Flavor[Csv] = CsvFlavor
    
    csvFlavor.read[Int]("Hea,vy")
    // res2: Int = 0
    

    总的来说,最好是远离 类型成员 ,因为它们更复杂,用于更高级的东西,如 路径依赖类型 .


    免责声明: 我与类型成员相处不好(仍在学习),这可能会激励我使用不同的选择。-在任何情况下,我希望你能应用一些类似于你实际问题的东西。 .

        2
  •  1
  •   Luis Miguel Mejía Suárez    7 年前

    WIRE 类型参数,并通过类型成员或 Maker

    import scala.reflect.runtime.universe._
    
    trait Flavor[WIRE] {
      def read[T](wire: WIRE)(implicit tt: TypeTag[T]): T
    }
    
    trait Maker {
      type O
      def make(): Flavor[O]
    }
    
    object StringMaker extends Maker {
      type O = String
      def make(): Flavor[O] = StringFlavor()
    }
    
    case class StringFlavor() extends Flavor[String] {
      def read[T](wire: String)(implicit tt: TypeTag[T]): T = {
        if(tt.tpe =:= typeOf[Int]) {
          5.asInstanceOf[T]
        } else
          throw new Exception("Boom")
      }
    }
    
    object FantasticThing {
      def apply(): Flavor[String] = StringMaker.make()
      def apply(maker: Maker): Flavor[maker.O]  = maker.make() // Path dependent type.
    }
    
    object RunMe extends App {
      val thing: Flavor[String] = FantasticThing(StringMaker)
      thing.read[Int]("Heavy") // res0: Int = 5
    }
    

    编辑:未将任何参数apply()添加到此anwser。如果使用了maker的默认值(例如StringMaker),则会出现编译错误,因为参数“Heavy”现在应该是maker.O类型。添加no arg apply解决了这个问题,同时为调用者提供了相同的体验。

    推荐文章