type generic classes scala types companion-object

scala - generic - objeto compañero a una clase privada: ¿por qué no es válido?



self notation scala (2)

Creo que no quieres una clase privada, sino una clase con un constructor privado.

class A private() object A { def apply = dual lazy val dual = new A }

Ahora su clase es "visible" al código externo, pero solo su objeto compañero puede crear instancias de él.

Necesitaba dos instancias que tuvieran acceso a otros privados. Naturalmente pensé en un objeto compañero que otorga acceso a una única instancia de su clase compañera. La clase en sí misma la hice privada, por lo que los usuarios no pueden simplemente crear instancias usando new .

object A { def apply = dual lazy val dual = new A } private class A { //some irrelevant logic... }

Este código no se compila. Obtengo: la clase A escapa a su ámbito de definición como parte del error de tipo A , que realmente no entiendo. mi solución actual era definir un rasgo con cada declaración de método que la clase debería tener y hacer que la class A extienda ese rasgo, mientras que dual es del tipo de rasgo, y no del tipo de class A

¿Cuál es el problema teórico que me falta aquí? ¿Por qué está prohibido?


La solución de Paolo es buena (+1), pero no explicó el mensaje de error, así que déjame intentarlo. El problema radica en el hecho de que cada método necesita un tipo de retorno. Su definición original de apply y dual devolvió un objeto de class A , por lo tanto, el tipo de retorno implícito de ambos fue A Eso implica que A debe ser visible para los clientes. ¿De qué otra forma podrían llamar a la función o acceder al val ? Además, como ambos, y su objeto principal también son públicos, son visibles a nivel mundial. Sin embargo, declaró A private que significa que no debe ser visible fuera de su paquete. Así que hay un conflicto que no puede ser resuelto por el compilador.

La regla general es que todos los parámetros y tipos de retorno de funciones / miembros deben tener (al menos) el mismo alcance de visibilidad que el miembro referente *. Por lo tanto, una forma trivial de resolver este problema sería reducir la visibilidad de apply y dual to private . Esto satisfaría al compilador, pero no a usted :-)

Su solución soluciona el problema cambiando el tipo de retorno estático a un rasgo public , que por lo tanto tiene la misma visibilidad que los miembros que se refieren a él. El tipo dinámico del objeto devuelto sigue siendo la class A , sin embargo, esto no necesita ser visible para los clientes. Este es un ejemplo clásico del principio "programa para interfaces, no implementaciones" .

Tenga en cuenta que para aplicar este principio en toda su extensión, uno podría convertir la class A en una clase interna private de object A , por lo que es inaccesible incluso para otras clases dentro del mismo paquete:

trait A { //... } object A { def apply: A = dual lazy val dual: A = new AImpl private class AImpl extends A { //some irrelevant logic... } }

* Para ser pedante, la clase / objeto adjunto puede reducir la visibilidad de sus miembros, como aquí:

private class Holder { def member = new Hidden } private class Hidden

donde el member es public pero su clase adjunta es private , ocultando efectivamente a sus miembros del mundo externo. Así que el compilador no emite ninguna queja aquí.