Частный конструктор класса case Scala, но метод public apply

Если у меня есть следующий класс case с частным конструктором, и я не могу получить доступ к методу apply в сопутствующем объекте.

case class Meter private (m: Int)

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

Есть ли способ использовать класс case с частным конструктором, но сохранить созданный метод apply в общедоступном соавторе?

Я знаю, что нет разницы (в моем примере) между двумя вариантами:

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

но я хочу запретить первый вариант.

-- редактировать --

Удивительно, но следующие работы (но не совсем то, что я хочу):

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

Ответы

Ответ 1

Здесь используется метод создания частного конструктора и общедоступного метода.

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

Ответ 2

Это возможно с некоторыми неявными трюками:

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

создал другой конструктор (и применил подпись метода), но гарантирует, что параметр может быть только Int.

И после того, как у вас есть класс case с функциями класса case (с совпадением шаблонов, hashcode & equals), исключите конструктор по умолчанию:

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)

Некоторые возможные проблемы с примитивными типами и тегами типа, описанные здесь

Ответ 3

Кажется, что запрошенное поведение (частный конструктор, но public .apply) может быть таким, как это реализует Scala 2.12.

Я пришел к этому с противоположного угла - хотел бы, чтобы частный класс классов классов также блокировал метод .apply. Причины здесь: https://github.com/akauppi/case-class-gym

Интересно, как различаются случаи использования.