Value Classes - toString

591 views
Skip to first unread message

Kris Massey

unread,
Jul 3, 2013, 10:52:47 AM7/3/13
to scala...@googlegroups.com
Hi All,

I've just been playing around with Value Classes and I'm seeing behavior I didn't expect...chances are this is down to my last of understanding, as I'm new to Scala.

Given a Class definition of

class Username(underlying: String) extends AnyVal

When you run the line below

println(Username("dave"))

I expected the toString method to return 'dave' as the value, however it seems to be printing out the object reference. I had expected the new AnyVal to behave like a String object...

Am I missing something?

I know its easy enough to override the toString method like below :

class Username(underlying: String) extends AnyVal {
  override def toString : String =  {underlying.toString}
}


 
However given a large number of value classes it seems a little more verbose than I expected.

If I'm misunderstanding the purpose of Value Classes can anyone point me in the direction of a good explanation?

Many thanks
Kris

Stefan Langer

unread,
Jul 3, 2013, 10:55:23 AM7/3/13
to Kris Massey, scala-user

I think what you want are case classes.

--
You received this message because you are subscribed to the Google Groups "scala-user" group.
To unsubscribe from this group and stop receiving emails from it, send an email to scala-user+...@googlegroups.com.
For more options, visit https://groups.google.com/groups/opt_out.
 
 

Johannes Rudolph

unread,
Jul 3, 2013, 10:56:58 AM7/3/13
to Kris Massey, scala-user
Hi Kris,


On Wed, Jul 3, 2013 at 4:52 PM, Kris Massey <krism...@gmail.com> wrote:
I expected the toString method to return 'dave' as the value, however it seems to be printing out the object reference. I had expected the new AnyVal to behave like a String object...

It's more the other way round: as a developer using AnyVal should mostly be transparent to you, i.e. value classes should behave like normal classes. However, when possible the compiler will try to make the cost of real classes go away whenever possible, i.e. everywhere but in cases where you would be able to observe it.
 
--
Johannes

-----------------------------------------------
Johannes Rudolph
http://virtual-void.net

Kris Massey

unread,
Jul 3, 2013, 11:14:53 AM7/3/13
to scala...@googlegroups.com
@slmusk
I'm not sure Case classes are what I'm after...

@Johannes
What I'm attempting to do is 'Strong' type values, so I dont have lots of String and Ints roaming around my code. As I believe Strong typing is a great addition to self documenting code.


It might be clearer if I give the example in Java of how I would code things and then figure out how that would translate to Scala.


Java Code:

Given a base class such as

public abstract class StringValue {

    protected final String value;

    protected StringValue(String value) {
        this.value = value;
    }

    public String getValue() {
        return value;
    }

    public String toString() {
        return value;
    }
    //hashcode and equals methods as needed
}

Then to allow for Strong typing such as

public class Email extends StringValue {
    public Email(String value) {
        super(value);
    }
}

Where Email can be exchanges for Password (where toString would be overridden to stop the value accidentally being logged), FirstName, LastName...etc so the types behave like a String but are 'Strong' typed to make things clearer.


I assumed (incorrectly it would seem) that the basics of that outline could be achieved by extending the AnyVal and creating simple Value Classes

Hopefully that gives a little more context on what I'm trying to achieve, as I was a little light in the initial question.

Many thanks
Kris

Alec Zorab

unread,
Jul 3, 2013, 11:27:37 AM7/3/13
to Kris Massey, scala-user
scala>   trait TypedValue[T] extends AnyRef {
     |     def value:T
     |     override def toString = value.toString
     |   }
defined trait TypedValue

scala>   case class Email(value:String) extends TypedValue[String]
defined class Email

scala> Email("a...@b.com")
res5: Email = a...@b.com


something like that?



It actually occurs to me that I'm not sure if that actually makes the Email into a value class. Possibly you need to do this:


scala>   trait TypedValue[T] extends Any {
     |     def value:T
     |     override def toString = value.toString
     |   }
defined trait TypedValue

scala>

scala>   case class Email(value:String) extends AnyRef with TypedValue[String]
defined class Email

scala> Email("a...@b.com")
res6: Email = a...@b.com


I don't know.


--

Simon Ochsenreither

unread,
Jul 3, 2013, 11:34:51 AM7/3/13
to scala...@googlegroups.com, Kris Massey

AnyRef

Not sure this is what you want to do ...

Alec Zorab

unread,
Jul 3, 2013, 11:35:26 AM7/3/13
to Simon Ochsenreither, scala-user, Kris Massey
uhhh. Slip of the fingers...


On 3 July 2013 16:34, Simon Ochsenreither <simon.och...@gmail.com> wrote:

AnyRef

Not sure this is what you want to do ...

--

Josh Suereth

unread,
Jul 3, 2013, 6:49:24 PM7/3/13
to Alec Zorab, scala-user, Kris Massey

If you want a value class, you must explicitly extend AnyVal.  Universal traits can be extended by non-value classes.

Also, toString, iirc, is treated as any other method on a value class.  It receives a staticish method that can be called to remove the object ref.

Please check out the value class SIP for details on special casing of hashcode and equals.  I think those are the only ones that get special cased....

Stefan Wagner

unread,
Jul 3, 2013, 8:52:35 PM7/3/13
to scala...@googlegroups.com
On 03.07.2013 16:52, Kris Massey wrote:
> ...
> I expected the toString method to return 'dave' as the value, however it
> seems to be printing out the object reference. I had expected the new
> AnyVal to behave like a String object...

Scala and Java don't print references or memory locations. The value you
observe is the hashcode in hexadezimal:


scala> class Cat (val name: String = "Minusch")
scala> val c = new Cat
c: Cat = Cat@9706b6

scala> println (c)
$line10.$read$$iw$$iw$Cat@9706b6

scala> println (c.hashCode)
9897654

scala> Integer.parseInt ("9706b6", 16)
res12: Int = 9897654


Michael Thaler

unread,
Jul 4, 2013, 1:53:15 AM7/4/13
to Stefan Wagner, scala-user
You could do

scala> case class Username(underyling: String) extends AnyVal
defined class Username

scala> println(Username("Dave"))
Username(Dave)

which automatically generates a toString, hashCode and equals methods for you. However, it will print ClassName(argument).

Best regards,
Michael

Alec Zorab

unread,
Jul 4, 2013, 4:18:08 AM7/4/13
to michael...@physik.tu-muenchen.de, Stefan Wagner, scala-user
What I'd originally intended to type before I started wildly flailing at the jeyboard was this:

scala>   trait TypedValue[T] extends Any {
     |     def value: T
     |     override def toString = value.toString
     |   }
defined trait TypedValue

scala>   case class Email(value: String) extends AnyVal with TypedValue[String]
defined class Email

scala>   Email("a...@b.com")
res0: Email = a...@b.com

But I couldn't quite work out how to prove or disprove the AnyValness of the resulting class...


Kris Massey

unread,
Jul 4, 2013, 6:02:56 AM7/4/13
to scala...@googlegroups.com
Thanks everyone,

That gives me a lot to go on, with the different options I can take. I think I'll take the approach outlined by AlecZorab as it feel like the right approach for what I'm trying to achieve.

Thanks again
Kris
Reply all
Reply to author
Forward
0 new messages