web-dev-qa-db-fra.com

Scala case class constructeur privé mais méthode publique d'application

Si j'ai la classe de cas suivante avec un constructeur privé et que je ne peux pas accéder à la méthode apply dans l'objet compagnon.

case class Meter private (m: Int)

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

Est-il possible d'utiliser une classe de cas avec un constructeur privé tout en conservant la méthode apply générée dans public compagnon?

Je suis conscient qu'il n'y a pas de différence (dans mon exemple) entre les deux options:

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

mais je veux interdire la première option.

-- modifier --

Étonnamment, les œuvres suivantes (mais ce n’est pas vraiment ce que je veux):

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

Voici la technique pour avoir un constructeur private et une méthode d'application public.

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

Informations de base constructeur-privé-et-protégé-en-scala

40
Farmor

C'est possible avec quelques astuces implicites:

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

créé un autre constructeur (et appliquer la signature de méthode) mais garantit que ce paramètre ne peut être que Int.

Et après que vous ayez une classe de cas avec des fonctionnalités de classe de cas (avec correspondance de modèle, hashcode & equals), excluez le constructeur par défaut:

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)

OU avec marquage de type (implémentation naïve):

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

Cela fonctionne comme prévu:

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)

Quelques problèmes possibles avec les types primitifs et les balises de type décrits ici

0
Yuriy

Il semble que le comportement demandé (constructeur privé mais .apply public) puisse être la façon dont Scala 2.12 les implémente.

J'y suis arrivé sous l'angle opposé - j'aimerais qu'un constructeur de classe de cas privé bloque également la méthode .apply. Raisons ici: https://github.com/akauppi/case-class-gym

Intéressant, comment les cas d'utilisation diffèrent.

0
akauppi