9
class foo(val x:Int){
  def convertToInt(z:string) = {do somthing to convert a string to an integer}
  def this(y:string) = this(convertToInt(y))
}

calling convertToInt in auxiliary constructor (this(y:string)) causes this error:

error: not found: value convertToInt

I know I can use a singleton object and pack all static functions like convertToInt into it, but is it a good solution?

object foo{
    def convertToInt(z:string) = {do somthing to convert a string to an integer}
}   
class foo(val x:Int){
    def this(y:string) = this(foo.convertToInt(y))
}

3 Answers3

14

I think in this case the best solution would be to use factory methods instead of public constructor.

So you can define your constructor private and provide factory apply methods in companion object:

class Foo private (val x:Int) 

object Foo {
    def apply(i: Int) = new Foo(i)
    def apply(s: String) = new Foo(convertToInt(s))

    def convertToInt(s: String) = s.toInt
}   

println(Foo(512).x)
println(Foo("256").x)

You can find more information about constructor vs factory method here:

Constructors vs Factory Methods

It's the same for Scala.

Update

As an example of alternative solution I made very generic solution. Foo class can now work with any class that ever existed or can be created in future, assuming, that this type can be converted (you can define how it should be converted) to/from Int:

trait Convertable[From, To] {
    def convert(from: From): To
}

object Convertable {
    implicit val intString = new Convertable[Int, String] {
        def convert(from: Int) = from toString // your logic here
    }
    
    implicit val stringInt = new Convertable[String, Int] {
        def convert(from: String) = from toInt // your logic here
    }

    implicit def self[T] = new Convertable[T, T] {
        def convert(from: T) = from
    }
}

case class Foo[T](original: T)(implicit toInt: Convertable[T, Int], fromInt: Convertable[Int, T]) {
    val x: Int = toInt convert original
    def toOriginal = fromInt convert x
}


println(Foo(512) x)
println(Foo("256") x)

(I could define toOriginal by just returning = original, but it would be too boring :)

As you can see, this solution is generic and more complicated. But as far as I saw, many application need some kind of conversion between different primitive values and/or classes. So in many cases it's sutable (and may be event considered very good) solution for many cases and may be for your also. But it's often impossible to tell what's "the best" solution for all possible cases.

Community
  • 1
  • 1
tenshi
  • 26,268
  • 8
  • 76
  • 90
  • if we have another static method like `def convetBackToString(i:Int)=i.toString` we must use this client code for call it: `Foo.convertToString(Foo(110))` is there any way for make it simpler like `Foo(110).convertToString`? – Mohammad Reza Esmaeilzadeh Feb 16 '11 at 09:20
  • @Esmaeilzadeh: There are number of ways to solve this, but in order to find the best one, I need to know wider scope of the problem. The most simple ans straightforward solution would be to `override def toString = convetBackToString(x)` in `class Foo` – tenshi Feb 16 '11 at 09:35
  • it seams a general solution is creating some methods (in this case toString ...) that act as a proxy for object methods and call methods of singleton object, is it the best solution? – Mohammad Reza Esmaeilzadeh Feb 16 '11 at 09:53
  • @Esmaeilzadeh: It's hard for me to tell whether it's best solution or not because I don't know the problem that this solution tries to solve. Sorry, but information you have provided is not enough. – tenshi Feb 16 '11 at 11:18
  • I would suggest to use the type `From => To` instead of creating a `Convertable` type. If you declare such a function type implicit, you will also have many such implicit conversions in scope automatically, like for example `Int => Long`. – Madoc Feb 18 '11 at 18:31
  • @Madoc: I also thought about this, but such behavior sometimes can also be a problem. If you have you very special conversion rules, then all these default conversions can only cause problems. But it's highly depends on context and I believe that for the most cases default conversions like `Int=> Long` are acceptable. – tenshi Feb 18 '11 at 19:00
2

by using angle's offer about factory method instead of Auxiliary Constructor:

class Foo(val s:String) {
      val s = ""
      def bar2:String = s+s
      def bar3:List[Char] = s.toList
}

object Foo extends{
      def bar1(y:List[Char]):String =y.mkString
      def apply(s:String)= new Foo(s)
      def apply(y:List[Char])= new Foo(bar1(y))
}

client code:

val foo1 = Foo(List('a','b'))
println(foo1.s)
println(foo1.bar2)
println(foo1.bar3)
0

Your solution isn't that bad. After all, convertToInt is similar to a static method in Java. Personally I don't like auxiliary constructors, so I'd normally prefer Easy Angels solution as well. However if you plan to inherit from your class later, the companion object approach won't "scale" for the derived class, you would have to reimplement that method. In that case you should stick with your solution.

Theoretically you could put that method in a separate trait and extend it, but I wouldn't recommend this. Using inheritance should be limited to cases when there is a real dependency, not just for "convenience".

Landei
  • 54,104
  • 13
  • 100
  • 195