Constructor privado y protegido en Scala

109

He sentido curiosidad por el impacto de no tener un constructor principal explícito en Scala, solo el contenido del cuerpo de la clase.

En particular, sospecho que el patrón de constructor privado o protegido, es decir, controlar la construcción a través del objeto complementario u otra clase o métodos de objeto, podría no tener una implementación obvia.

¿Me equivoco? Si es así, ¿Cómo se hace?

Don Mackenzie
fuente
Podría tener un singleton Scala (con la palabra clave de objeto, es decir) y definir su clase como privada dentro de ese singleton, y tener métodos del singleton para construir sus objetos.
Paggas
@Paggas, desafortunadamente cuando devuelve una instancia de una clase marcada como privada fuera de su alcance, no se compilará, incluso cuando se devuelva desde un método de su objeto complementario dentro del alcance.
Don Mackenzie
Esto se hace con bastante profusión en todo el código fuente de Scalaz. El concepto también se conoce como un tipo de datos algebraicos abstractos .
Tony Morris

Respuestas:

190

Puede declarar el constructor predeterminado como privado / protegido insertando la palabra clave adecuada entre el nombre de la clase y la lista de parámetros, así:

class Foo private () { 
  /* class body goes here... */
}
Aleksander Kmetec
fuente
Gracias Aleksander, ¿podría decirme si esto se presenta en uno de los libros de Scala o en la especificación del idioma? Lo siento, todavía no puedo votar a favor.
Don Mackenzie
Acabo de echar un vistazo a la explicación de los constructores de "Programming Scala" (páginas 92-95) y no veo que se mencione allí. De hecho, encontré la respuesta a su pregunta en un registro de cambios antiguo, pero nunca antes lo había visto mencionado en ningún otro lugar. Enlace: scala-lang.org/node/43#2.4.0
Aleksander Kmetec
18
Pag 414 de "Programación en Scala". Página 97 de la Scala de programación de Wampler. Página 60 de la Scala de programación de Subramaniam. No tengo un PDF de Beginning Scala conmigo en este momento para verlo.
Daniel C. Sobral
Oh, lo veo ahora en la página 97. Gracias.
Aleksander Kmetec
1
Gracias a ambos por la investigación adicional, tengo el libro de Wampler, pero solo en mi teléfono y claramente no lo he leído a fondo, pero he descubierto que complementa el libro de Odersky sorprendentemente bien.
Don Mackenzie
64

La respuesta de Aleksander es correcta, pero la programación en Scala ofrece una alternativa adicional:

sealed trait Foo {
 // interface
}

object Foo {
  def apply(...): Foo = // public constructor

  private class FooImpl(...) extends Foo { ... } // real class
}
Daniel C. Sobral
fuente
18
Apareciendo años después para decir: Creo que esta es una buena respuesta a la pregunta, pero una mala solución al problema. Si algún programador futuro estuviera en el código de Aleksander, diría "Ah, el constructor principal es privado pero otros constructores no lo son". Si ese programador mirara el código de Daniel, diría: "Ah, están usando un patrón de fábrica para compensar la incapacidad de Scala de marcar los constructores predeterminados como privados. ¡Espera, Scala puede marcar los constructores predeterminados como privados!" ¿¡¿aqui?!?" En otras palabras, una mala relación WTF / LOC.
Malvolio
20
@Malvolio No estoy del todo de acuerdo. Este patrón no solo hace que el constructor principal sea privado, sino también la implementación , lo que obliga al usuario a usar la interfaz (rasgo). Eso tiene su propio valor. En cuanto a alguien que piensa algo porque no conoce el idioma, ¡tonterías! Para citar a Kenny Tilton, ¡ aprende el maldito idioma !
Daniel C. Sobral
7
Debería mencionarse en alguna parte que este enfoque significa no utilizar la newpalabra clave.
Travis Parks
1
Una advertencia con este enfoque es que alguien aún podría crear una instancia de Foo a través de su propia implementación. Eso podría verse como una ventaja o una desventaja dependiendo del motivo para controlar la construcción.
aij
1
@aij Es cierto, así que lo hice para que eso no suceda más. :)
Daniel C. Sobral