【问题标题】:How to restrict a method only to being used with a specific type?如何将方法限制为仅用于特定类型?
【发布时间】:2019-05-02 10:49:03
【问题描述】:

假设我下面有一个案例类

case class SomeCaseClass[M] private (
  value: String
) 

在另一个文件中,我有以下特征和对象。

trait SomeTrait[A] {
  def get(oldId: String): A
   :
}

object SomeObject {
  private[this] def init[A](): SomeTrait[A] = new SomeTrait[A] {
    def get(oldId: String): A = id(oldId)
     :
  }

  val aaa: SomeTrait[String] = init[String]()
  val bbb: SomeTrait[SomeCaseClass[String]] = init[SomeCaseClass[String]]()
}

我应该如何修改代码,以便将 init 方法限制为仅与 SomeCaseClass[_] 类型一起使用,而不是与上面的 String 等任何类型一起使用?

理想情况下,对代码进行一些修改,val aaa: SomeTrait[String] = init[String]() 行应该会导致编译错误。

【问题讨论】:

    标签: scala


    【解决方案1】:

    这是我想出的:

    case class SomeCaseClass[M] private (
      value: String
    ) 
    
    trait SomeTrait[A] {
      def get(oldId: String): A
    }
    
    private[this] def init[A <: SomeCaseClass[_]](): SomeTrait[A] = new SomeTrait[A] {
      def get(oldId: String): A = ???
    }
    
    val aaa: SomeTrait[String] = init[String]() // Will fail
    val bbb: SomeTrait[SomeCaseClass[String]] = init[SomeCaseClass[String]]()
    

    失败了

    ScalaFiddle.scala:16: 错误:类型参数 [String] 不符合方法 init 的类型参数边界 [A <: scalafiddle.this.somecaseclass forsome type _>

    你可以查看这个scalafiddle

    我不知道这是否是最好的方法,但init[A &lt;: SomeCaseClass[_]] 正在添加一个绑定到A 的类型,并强制A 成为SomeCaseClass 的子类。不过我很想知道是否有更好的方法。

    【讨论】:

      【解决方案2】:

      您可以通过使用隐式参数来强制类型参数等于某个类型B

      def foo[A](implicit e: A =:= B): …
      

      另见this question

      为这个答案增加更多价值。 以下代码显示了如何使用隐式参数e: A =:= StringA 转换为String

      def bar(b: String): Unit = println(b)
      def foo[A](a: A)(implicit e: A =:= String): Unit = {
        bar(e(a))
      }
      
      foo("hi")  //compiles
      
      foo(5)     //error: Cannot prove that scala.this.Int =:= String.
      

      回答 OP 遇到的问题

      这个问题就简单多了:让方法参数化只在SomeCaseClass[A]的参数A中,而不是使用整个类型SomeCaseClass[A]作为类型参数:

      private[this] def init[A](): SomeTrait[SomeCaseClass[A]] = new
        SomeTrait[SomeCaseClass[A]] {
          def get(oldId: String): SomeCaseClass[A] = ???
        }
      

      【讨论】:

      • 不知道=:=。确切地说,对于 OP,我认为这是最接近的解决方案。
      • 您能否提供一个 sn-p 说明如何将其应用于我的案例?
      • @d-_-b 您的问题可能要简单得多。请查看我的编辑。
      • @ziggystar 有了这个修改,它仍然可以编译。即init[Int]()init[String]() 不会引发编译错误。我错过了什么吗?
      • @d-_-b 是的,现在它以每种类型作为参数进行编译。但是给定任何类型的A,您将只在内部使用SomeCaseClass[A]。这不就是你想要实现的吗:val aaa: SomeTrait[SomeCaseClass[String]] = init[String]() 这意味着你不能写不需要的代码;这比让不需要的代码在编译时失败更好。
      【解决方案3】:

      这是基于上面的答案:

      case class SomeCaseClass[M] private (
        value: String
      ) 
      
      trait SomeTrait[A] {
        def get(oldId: String): SomeCaseClass[A]
      }
      
      private[this] def init[A](): SomeTrait[A] = new SomeTrait[A] {
        def get(oldId: String): SomeCaseClass[A] = ???
      }
      
      val aaa: SomeTrait[String] = init[String]()
      

      (https://scalafiddle.io/sf/KuXZc0h/3)

      这不允许SomeCaseClass 以外的其他类型与SomeTrait 一起使用。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2018-08-01
        • 1970-01-01
        • 2021-08-10
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2016-04-17
        • 2016-07-22
        相关资源
        最近更新 更多