abstract case class & copy method

1,567 views
Skip to first unread message

Normen Müller

unread,
Feb 18, 2011, 10:41:54 AM2/18/11
to scala...@googlegroups.com
He,

why do I loose the copy method for an abstract case class?

scala> abstract case class Field(val name: String, val value: String)
defined class Field

scala> case class StrField(override val name: String, override val value: String) extends Field(name, value)
warning: there were deprecation warnings; re-run with -deprecation for details
defined class StrField

scala> val f: Field = StrField("foo", "bar")
f: Field = StrField(foo,bar)

scala> val g = f.copy(name = "baz")
<console>:10: error: value copy is not a member of Field
val g = f.copy(name = "baz")
^

In addition I realized that subclassing case classes is deprecated. So, I guess, Field should rather be a ``pure abstract class'' (i.e. no case class). But, still, I have the same problem.

Any suggestions?

Cheers,
/nm

Dennis Haupt

unread,
Feb 18, 2011, 10:58:50 AM2/18/11
to "Normen Müller", scala...@googlegroups.com
afaik case classes are intended to be leafs. it should work of field is NOT a case class

-------- Original-Nachricht --------
> Datum: Fri, 18 Feb 2011 16:41:54 +0100
> Von: "Normen Müller" <normen....@googlemail.com>
> An: scala...@googlegroups.com
> Betreff: [scala-user] abstract case class & copy method

Dennis Haupt

unread,
Feb 18, 2011, 11:01:15 AM2/18/11
to Dennis Haupt, scala...@googlegroups.com, normen....@googlemail.com
yes:
object GenericVoodoo {



def main(args: Array[String]) {
val foo = casebase("hi").copy("jo")
println
}

}

class base(val name:String)
case class casebase(override val name:String) extends base(name)


-------- Original-Nachricht --------
> Datum: Fri, 18 Feb 2011 16:58:50 +0100
> Von: "Dennis Haupt" <h-s...@gmx.de>
> An: "Normen Müller" <normen....@googlemail.com>, scala...@googlegroups.com
> Betreff: Re: [scala-user] abstract case class & copy method

Philippe Lhoste

unread,
Feb 18, 2011, 11:21:31 AM2/18/11
to scala...@googlegroups.com

Yes, this kind of code is deprecated.
And no, you don't have the same problem.
If you remove "case" from the first line (ie. Field is just an abstract class), you have
to write either:

val g = f.asInstanceOf[StrField].copy(name = "baz")

or, of course,

val f: StrField = StrField("foo", "bar")

The behavior doesn't change from Java, here.

--
Philippe Lhoste
-- (near) Paris -- France
-- http://Phi.Lho.free.fr
-- -- -- -- -- -- -- -- -- -- -- -- -- --

Normen Müller

unread,
Feb 18, 2011, 12:33:42 PM2/18/11
to Philippe Lhoste, Dennis Haupt, scala...@googlegroups.com
He

thanks for your help and yes initiating ``copy'' directly, i.e. ``val foo = casebase("hi").copy("jo")'' works just fine. And ``f.asInstanceOf[StrField].copy(name = "baz")'' also works but I don't know the fact that ``f'' is an instance of ``StrField''.

I'll try to describe my problem again:

scala> abstract class Field[T](val name: String, val value: T)
defined class Field

scala> case class StrField(override val name: String, override val value: String) extends Field[String](name, value)
defined class StrField

scala> case class IntField(override val name: String, override val value: Int) extends Field[Int](name, value)
defined class IntField

scala> def doSomething(f: Field[_]): Field[_] = f.copy(name = "processed")
<console>:6: error: value copy is not a member of Field[_$1]
def doSomething(f: Field[_]): Field[_] = f.copy(name = "processed")
^
For sure I could use pattern matching like:

scala> def doSomething(f: Field[_]): Field[_] = f match {
| case s: StrField => s.copy(name = "processed")
| case i: IntField => i.copy(name = "processed")
| case _ => error("unknown type")
| }
doSomething: (f: Field[_])Field[_]

BUT there would be a lot of ``cases'' in my case :(

Isn't there a general way to tell the compiler that each Field has a copy method but I don't wanna implement those but use the Scala's generated ones.

Thanks in advance,
/nm


On Feb 18, 2011, at 5:21 PM, Philippe Lhoste wrote:

HamsterofDeath

unread,
Feb 18, 2011, 1:36:41 PM2/18/11
to scala...@googlegroups.com
can't you make it abstract in the base class?
if everything else fails, reflection is always an option.

Philippe Lhoste

unread,
Feb 18, 2011, 4:19:17 PM2/18/11
to scala...@googlegroups.com
On 18/02/2011 18:33, Normen M�ller wrote:
> Isn't there a general way to tell the compiler that each Field has a
> copy method but I don't wanna implement those but use the Scala's
> generated ones.

I thought about defining an abstract copy function in the Field class,
but there might be some magic here (or just my lack of understanding, as
I am a beginner), as the compiler complains that the f.copy(name = "b")
doesn't have enough parameters:

abstract class Field(val name: String, val value: String)
{
def copy(name: String, value: String): Field


}
case class StrField(override val name: String, override val value:
String) extends Field(name, value)

val f: StrField = StrField("foo", "bar")

val g = f.copy(name = "baz")

Not sure how to workaround this one.

Normen Müller

unread,
Feb 19, 2011, 5:59:34 AM2/19/11
to Philippe Lhoste, HamsterofDeath, scala...@googlegroups.com
On Feb 18, 2011, at 10:19 PM, Philippe Lhoste wrote:

> abstract class Field(val name: String, val value: String) {
> def copy(name: String, value: String): Field
> }
> case class StrField(override val name: String, override val value: String) extends Field(name, value)
> val f: StrField = StrField("foo", "bar")
> val g = f.copy(name = "baz")

Yes, sounds good. BUT:

scala> abstract class Field[T](val name: String, val value: T) {
| def copy(name: String = this.name, value: T = this.value)
| }
defined class Field

scala> case class StrField(override val name: String, override val value: String) extends Field[String](name, value)
<console>:6: error: class StrField needs to be abstract, since method copy in class Field of type (name: String,value: String)Unit is not defined
case class StrField(override val name: String, override val value: String) extends Field[String](name, value)

So I tried this one but I am not sure if by this approach Field is still like an abstract class. I basically want to restrict to have direct Field class instances, i.e. Field should be abstract:

scala> sealed class Field[T] protected(val name: String, val value: T) {
| def copy(name: String = this.name, value: T = this.value) = new Field(name, value)
| override def toString() = "Field[T]("+this.name+","+this.value+")"
| }
defined class Field

scala> case class StrField(override val name: String, override val value: String) extends Field[String](name, value)
defined class StrField

scala> val s = StrField("foo", "bar")
s: StrField = Field[T](foo,bar)

scala> val t = s.copy(name = "baz")
t: Field[String] = Field[T](baz,bar)

So this could be a solution. What do the others think?

/nm

Philippe Lhoste

unread,
Feb 20, 2011, 5:15:13 AM2/20/11
to scala...@googlegroups.com
On 19/02/2011 11:59, Normen M�ller wrote:
> Yes, sounds good. BUT:
>
> scala> abstract class Field[T](val name: String, val value: T) {
> | def copy(name: String = this.name, value: T = this.value)
> | }
> defined class Field
>
> scala> case class StrField(override val name: String, override val value: String) extends Field[String](name, value)
> <console>:6: error: class StrField needs to be abstract, since method copy in class Field of type (name: String,value: String)Unit is not defined
> case class StrField(override val name: String, override val value: String) extends Field[String](name, value)

Yes, I tried that (only default on size) and hit the same error. I am
not good enough (yet...) to be able to find the workaround you give...

> So I tried this one but I am not sure if by this approach Field is still like an abstract class. I basically want to restrict to have direct Field class instances, i.e. Field should be abstract:
>
> scala> sealed class Field[T] protected(val name: String, val value: T) {
> | def copy(name: String = this.name, value: T = this.value) = new Field(name, value)
> | override def toString() = "Field[T]("+this.name+","+this.value+")"
> | }
> defined class Field

Interesting. Not sure what the sealed implies there.

Ruediger Keller

unread,
Feb 20, 2011, 7:52:30 AM2/20/11
to Normen Müller, scala...@googlegroups.com
Hi,

Why don't you define your classes like this? I think this is much cleaner:


abstract class Field[T] {
def name: String
def value: T
}
case class StrField(name: String, value: String) extends Field[String]


Regarding your copy problem, unfortunately the Scala compiler doesn't
consider the automatically generated copy method as an implementation
of the abstract method, although it has the correct signature:


abstract class Field[T] {
def name: String
def value: T
def copy(n: String, t: T): Field[T]
}
case class StrField(name: String, value: String) extends Field[String] // Error


But, from your examples there seems to be no need for the class
hierarchy you define. You could just use a single case class. This is
further supported by your last post, where you accept the "solution"
of copying a FieldStr yielding a Field. But if that is the solution
you don't seem to have a need for all the XyzField classes in the
first place. So why don't you just go for the following?


case class Field[T](name: String, value: T)


If you really need the sub classes for something not shown in your
examples, then perhaps this helps you. In general you can only change
the name of your Fields because that is the only thing in common for
all Field classes. That suggests the following abstract method:


abstract class Field[T] {
def name: String
def value: T
def withName(newName: String): Field[T]
}

case class StrField(name: String, value: String) extends Field[String] {
def withName(newName: String) = copy(name = newName)
}


But then you need to define the method in all your leaf classes.
As a last resort, you can also use cloning and reflection, but that is
really ugly, so I can't really recommend it.


abstract class Field[T] extends Cloneable {
def name: String
def value: T
def withName(newName: String): Field[T] = {
val clonedField = this.clone
val nameField = getClass.getDeclaredField("name")
nameField.setAccessible(true)
nameField.set(clonedField, newName)
clonedField.asInstanceOf[Field[T]]
}
}
case class StrField(name: String, value: String) extends Field[String] {


Regards,
Ruediger


2011/2/18 Normen Müller <normen....@googlemail.com>:

Reply all
Reply to author
Forward
0 new messages