Scala case class private constructor but public apply method

流过昼夜 提交于 2019-11-29 22:55:43
Farmor

Here's the technique to have a private constructor and a public apply method.

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)
}

Background information private-and-protected-constructor-in-scala

It seems the requested behavior (private constructor but public .apply) may be the way Scala 2.12 implements these.

I came to this from the opposing angle - would like a private case class constructor also block the .apply method. Reasons here: https://github.com/akauppi/case-class-gym

Interesting, how use cases differ.

It is possible with some implicit tricks:

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

created some another constructor (and apply method signature) but guaranty that parameter can be only Int.

And after you have case class with case class features (with pattern matching, hashcode & equals) exclude default constructor:

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)

OR with type tagging (naive implementation):

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

It works as expected:

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)

Some possible issues with primitive types and type tags described here

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!