Scala案例类私有构造函数但公共应用方法

Posted

技术标签:

【中文标题】Scala案例类私有构造函数但公共应用方法【英文标题】:Scala case class private constructor but public apply method 【发布时间】: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?我刚刚在我的 2.10.0 REPL 中尝试过,val m2 = Meter(10) 没有给出任何错误] @LuigiPlinge 我正在使用 Scala 2.10.3 似乎是 case class Meter private (m: Int) 行导致错误,当声明为***对象 (scalafiddle.net/console/eb6fdc36b281b7d5eabf33396c2683a2) 但在另一个对象或 REPL (scalafiddle.net/console/cdc0d6e63aa8e41c89689f54970bb35f) 中声明时它可以工作跨度> 【参考方案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 描述的基本类型和类型标签的一些可能问题

【讨论】:

以上是关于Scala案例类私有构造函数但公共应用方法的主要内容,如果未能解决你的问题,请参考以下文章

为啥私有构造函数在案例类中仍然可见?

快学Scala 第六课 (类构造函数)

Scala:定义案例类构造函数时“覆盖受保护的val”导致错误

如何使用私有构造函数对类进行单元测试?

java内部私有类的构造函数

C++ - 构造函数重载 - 私有和公共