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
-------- 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
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
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
-- -- -- -- -- -- -- -- -- -- -- -- -- --
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:
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.
> 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
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.
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>: