【问题标题】:Scala case class private constructor but public apply methodScala案例类私有构造函数但公共应用方法
【发布时间】:2020-05-16 23:11:00
【问题描述】:

如果我有以下带有私有构造函数的案例类,并且我无法访问伴随对象中的应用方法。

case class Meter private (m: Int)

val m = Meter(10) // constructor Meter in class Meter cannot be accessed...

有没有办法使用带有私有构造函数的案例类,但将生成的应用方法保留在伴随的公共中?

我知道这两个选项之间没有区别(在我的示例中):

val m1 = new Meter(10)
val m2 = Meter(10)

但我想禁止第一个选项。

-- 编辑--

令人惊讶的是以下作品(但不是我真正想要的):

val x = Meter
val m3 = x(10) // m3  : Meter = Meter(10)

【问题讨论】:

标签: scala apply case-class private-constructor


【解决方案1】:

这是拥有 private 构造函数和 public apply 方法的技术。

trait Meter {
  def m: Int
}

object Meter {   
  def apply(m: Int): Meter = { MeterImpl(m) }
  private case class MeterImpl(m: Int) extends Meter { println(m) }
}

object Application extends App {
  val m1 = new Meter(10) // Forbidden
  val m2 = Meter(10)
}

背景资料private-and-protected-constructor-in-scala

【讨论】:

  • @senia,不,它没有。这是 new Meter(10){} 的编译错误;特质仪表是一个特质;不接受构造函数参数
  • 我错过了。您应该将Meter 定义为trait Meter { def m: Int } 以允许访问m。你也可以sealed
  • 我认为这是一个很好的解决方法,因为您可以从案例类中获得所要求的行为和一些好处(例如,实现 equals)。但与单例类相比,它有点重。
  • 在这种情况下println(Meter(1)) 不会导致> MeterImpl(1)?并不是说这不能回答问题,但我想要一个不会发生这种情况的解决方案。
【解决方案2】:

似乎请求的行为(私有构造函数,但公共 .apply)可能是 Scala 2.12 实现这些的方式。

我是从相反的角度得出的——希望私有案例类构造函数也阻止.apply 方法。原因在这里:https://github.com/akauppi/case-class-gym

有趣的是,用例有何不同。

【讨论】:

    【解决方案3】:

    有一些隐含的技巧是可能的:

    // first case 
    case class Meter[T] private (m: T)(implicit ev: T =:= Int)
    object Meter { 
      def apply(m: Int) = new Meter(m + 5) 
    }
    

    创建了另一个构造函数(并应用方法签名),但保证该参数只能是Int

    在您拥有具有案例类功能的案例类(具有模式匹配、哈希码和等于)之后,排除默认构造函数:

    scala> val m = Meter(10)
    m: Metter[Int] = Meter(15)
    
    scala> val m = new Meter(10)
    <console>:9: error: constructor Meter in class Meter cannot be accessed in object $iw
           val m = new Meter(10)
    

    或带有类型标记(朴素实现):

    trait Private
    case class Meter private (m: Integer with Private)
    object Meter {
      def apply(m: Int) = new Meter((m + 5).asInstanceOf[Integer with Private])
    }
    

    它按预期工作:

    val x = new Meter(10)
    <console>:11: error: constructor Meter in class Meter cannot be accessed in object $iw
                  new Meter(10)
                  ^
    
    val x = Meter(10)
    x: Meter = Meter(15)
    

    here 描述的基本类型和类型标签的一些可能问题

    【讨论】:

      猜你喜欢
      • 2013-12-30
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-02-08
      • 2012-02-04
      • 2017-04-30
      相关资源
      最近更新 更多