3

Suppose I have the following trait and object:

trait NoAnon {
    val i: Int
}

object NoAnon {
    def create = new NoAnon {
        val i = 123
    }
}

I would like to prevent anonymous instances of NoAnon from being created outside of the companion object. ie. only create in this example should be allowed to do this. I can enclose these within another object, and make the trait private to that object to accomplish this:

object Ctx {

    private[Ctx] trait NoAnon {
        val i: Int
    }

    object NoAnon {
        def create = new Ctx.NoAnon {
            val i = 123
        }
    }

}

Is it possible to do this without the enclosing object Ctx?

To be a little more clear, can traits mimic the functionality of a private abstract class constructor? That is, the following example, except with a trait.

abstract class NoAnon private[NoAnon] {
    val i: Int
}

object NoAnon {
    def create = new NoAnon {
        val i = 123
    }
}
Michael Zajac
  • 55,144
  • 7
  • 113
  • 138
  • make the trait (or its constructor) private to the package? – kiritsuku Apr 21 '15 at 14:36
  • That's not really much different than just making it private to an object, though. I'm thinking this is not possible as stated without side-stepping the issue. – Michael Zajac Apr 21 '15 at 15:32
  • @m-z is there any reason you don't like the proposed solutions? That is, per-object private trait and per-package private trait? – Ionuț G. Stan Apr 21 '15 at 15:57
  • @IonuțG.Stan Not particularly. It's a personal preference not to wrap them like that, but I'm more so just wondering if there is a better built-in way. – Michael Zajac Apr 21 '15 at 16:45

1 Answers1

2

Maybe you could use a class with a private constructor and extend that with your trait. You can even nest that class in the companion object:

trait NoAnon extends NoAnon.type#NoAnonCheck { 
    val i: Int
}

object NoAnon {
    class NoAnonCheck private[NoAnon]
    def create = new NoAnon { 
        val i = 1 
    }   
}

Then if you tried:

new NoAnon { val i = 2 }

You get:

error: constructor NoAnonCheck in class NoAnonCheck cannot be accessed in <$anon: NoAnonCheck with NoAnon>

But you can use NoAnon.create. Other than adding something like this, I don't think there is currently a pure way to do this in Scala.

Of course as you know and as mentioned in the comments, the other options are to make the the trait private to the scope of an enclosing object or package.

Ben Reich
  • 16,222
  • 2
  • 38
  • 59
  • This seems somewhat convoluted, but also seems like the only way to avoid an enclosing object / sealed & secluded trait. Clever, nonetheless. – Michael Zajac Apr 22 '15 at 02:14
  • I agree that this is definitely convoluted – probably too hokey for anything in production, in my opinion! – Ben Reich Apr 22 '15 at 11:41