views:

70

answers:

3

For example (maybe a bit clumsy from a real life view, but just to illustrate):

"User" is a case class containing user name and id. Id can be never set manually, and a User class instance with no id set has no sense.

A UserBase class maintains users base and has a "getUser (name : String) : User" method returning a consistent User instance.

No one other than a UserBase object can know (well, someone can, but really shouldn't rely on this knowledge) a user's id, so constructing a User instance manually makes no sense (and can cause errors in future if someone accidentally hardcodes this and forgets). Moreover, having an orphan User instance not tracked by a UserBase is also undesired.

So the task is to make calling UserBase.getUser the only way to get a User instance.

Can this be implemented in Scala?

+1  A: 

A case class automatically gets several features, including a companion object with an apply() method for constructing instances of the class. This is why you don't need "new" with case classes. If you try to make an explicit companion with apply() you will get error: method apply is defined twice

If you want to make your own factory method then you should not use case classes. You can still have all of the same features (toString, apply, unapply, etc) but you will have to implement them manually and to your own specification.

Ben Jackson
Thanks, a useful information to know. But this does not answer the question, which is to make neither a constructor nor "apply" available anywhere but in one particular (a different) class. In C++ I'd probably use "friend" definition (as far as I can remember, my C++ experience was many years ago) to make a private member available to another class, but I have no idea on how to reach this in Scala.
Ivan
In scala you can set the level of hierarchy for the `private` as `private[what]` where `what` can be an enclosing level of package or object hierarchy or `this`. Someone answered below with an example.
Ben Jackson
+2  A: 

You have to put the classes in the same package or make them part of the same class or object. Then:

object O {
  class C private[O] (val x: Int) { }
  object D { def apply(i:Int) = new C(i) }
  def getC(i:Int) = new C(i)
}

scala> O.D(5)
res0: O.C = O$C@5fa6fb3e

scala> new O.C(5)
<console>:10: error: constructor C cannot be accessed in object $iw
       new O.C(5)

scala> O.getC(5)
res1: O.C = O$C@127208e4
Rex Kerr
A: 

You don't actually clarify what a 'base' is in this context, but given your description it sounds like it's really nothing more than a factory for users.

The usual place to put a factory for a class is in the companion object (This is how case classes do it, but the technique isn't restricted to just case classes)

class User private(val id: Int, val name: String) {
  ...
}

object User {
  private def nextId() : Int = ...
  def apply(name: String) = new User(nextId(), name)
}

//now create one:
val u = User("Ivan")

Of course, if the User object is immutable (highly recommended), then there's very little reason to hide the id member. You're probably also going to want a (restricted) method to construct a User with a specified ID, mostly for reasons of unit testing.

Working with companions like this, it's also unlikely that you'll still need a distinct UserBase factory. Having your factory named the same as the instances it produces will result in cleaner code.

Kevin Wright