Paolo's solution is good (+1), but he didn't explain the error message, so let me try that. The problem stems from the fact that every method needs a return type. Your original definition of apply
and dual
returned an object of class A
, thus the implicit return type of both was A
. That implies that A
must be visible to clients - how else could they call the function or access the val
? Moreover, as both - and their parent object too - are public, they are globally visible. However, you declared A private
which means it must not be visible outside its package. So there is a conflict which can't be resolved by the compiler.
The general rule is that all parameter and return type of functions / members must have (at least) the same scope of visibility as the referring member itself*. Thus one trivial way to solve this problem would be to reduce the visibility of apply
and dual
to private
. This would satisfy the compiler, but not you :-)
Your solution gets around the problem by changing the static return type to a public
trait, which thus has the same visibility as the members referring to it. The dynamic type of the returned object is still class A
, however, this need not be visible to clients. This is a classic example of the principle "program to interfaces, not implementations".
Note that to apply this principle to the full extent, one could turn class A
into a private
inner class of object A
, thus making it innaccessible even for other classes within the same package:
trait A {
//...
}
object A {
def apply: A = dual
lazy val dual: A = new AImpl
private class AImpl extends A {
//some irrelevant logic...
}
}
* To be pedantic, the enclosing class / object may reduce the visibility of its members, like here:
private class Holder {
def member = new Hidden
}
private class Hidden
where member
is public
but its enclosing class is private
, effectively hiding its members from the external world. So the compiler emits no complaints here.