Let's say for instance that I have the following classes, is there a way to do the copy command without overriding the argument from the superclass?
I want to avoid override val arg1: String from the declaration of - case class MyCaseClass(override val arg1: String, arg2: String) extends MyAbstractClass
abstract class MyAbstractClass (val arg1: String = "bar 1")
case class MyCaseClass(arg2: String) extends MyAbstractClass
object MyMain {
def main(args: Array[String]): Unit = {
val myObj = MyCaseClass(arg2 = "bar 2")
println("a.arg1: " + myObj.arg1) // a.arg1: bar 1
println("a.arg2: " + myObj.arg2) // a.arg2: bar 2
val b = myObj.copy(arg1 = "try_1", arg2 = "try_2")
//>> "too many arguments (2) for method copy: (arg2: String)com.MyCaseClass
//>> Note that 'arg1' is not a parameter name of the invoked method."
}
}
Edit:
In my actual code I got hundreds of fields, that's why it's so important, I want to keep clean code and avoid hundreds of meaningless redundant overrides.
An alternative would be to NOT provide a value to arg1
trait MyAbstractClass {
val arg1: String // no value assigned...
}
case class MyCaseClass(
arg1: String = "bar 1", // ...no need for override val
arg2: String
) extends MyAbstractClass
Then
val myObj = MyCaseClass(arg2 = "bar 2")
println("a.arg1: " + myObj.arg1) // a.arg1: bar 1
println("a.arg2: " + myObj.arg2) // a.arg2: bar 2
val b = myObj.copy(arg1 = "try_1", arg2 = "try_2")
works without any issue. The only difference is that default value would have to be provided for arg1 in MyCaseClass constructor.
If you don't want to make arg1 a part of:
MyCaseClass.apply
MyCaseClass.unapply
myCaseClass.toString
myCaseClass.equals(anotherObject)
myCaseClass.hashcode
then probably using a case class is a bad idea in the first place, since case class is less of a "universal boilerplate generator" and more like a build-in way of defining records with a strong opinion what a record is. And there are alternatives to it like e.g. contraband with different opinions and decisions.
As #Mateusz Kubuszok suggested, that's might be the only option, which obviously doesn't make the code cleaner.
abstract class MyAbstractClass (val arg1: String = "bar 1")
case class MyCaseClass(val arg2: String) extends MyAbstractClass {
def copy(newArg1: String, newArg2: String): MyCaseClass =
new MyCaseClass(arg2 = newArg2) {
override val arg1: String = newArg1
}
}
object MyMain {
def main(args: Array[String]): Unit = {
val myObj = MyCaseClass(arg2 = "bar 2")
println("myObj.arg1: " + myObj.arg1) // myObj.arg1: bar 1
println("myObj.arg2: " + myObj.arg2) // myObj.arg2: bar 2
val mySecObj = myObj.copy(newArg1 = "try_1", newArg2 = "try_2")
println("mySecObj.arg1: " + mySecObj.arg1) // mySecObj.arg1: try_1
println("mySecObj.arg2: " + mySecObj.arg2) // mySecObj.arg2: try_2
}
}
Related
I was following along this tutorial by Jonas Bonér on how to achieve AspectJ like aspect oriented programming in Scala. The mixins got me quite confused with the abstract overrides. I eventually got the implementation working by matching parsePointcutExpression. However, The pointcutExpression matching is incorrect:
My Invocation case class:
package aspect2
import java.lang.reflect.Method
object aspect {
case class Invocation(val method: Method, val args: Array[AnyRef], val target: AnyRef) {
def invoke: AnyRef = method.invoke(target, args:_*)
override def toString: String = "Invocation [method: " + method.getName + ", args: " + args + ", target: " + target + "]"
}
}
My interceptor and advice methods for matching pointcutExpressions and logging: Note I want to match against "execution(* *.bar(..))".
package aspect2
import aspect2.aspect.Invocation
import org.aspectj.weaver.tools.{PointcutExpression, PointcutParser}
object InterceptorImpl {
trait Interceptor {
protected val parser = PointcutParser.getPointcutParserSupportingAllPrimitivesAndUsingContextClassloaderForResolution
protected def matches(pointcut: PointcutExpression, invocation: Invocation): Boolean = {
pointcut.matchesMethodExecution(invocation.method).alwaysMatches ||
invocation.target.getClass.getDeclaredMethods.exists(pointcut.matchesMethodExecution(_).alwaysMatches) ||
false
}
def invoke(invocation: Invocation): AnyRef
}
trait LoggingInterceptor extends Interceptor {
val loggingPointcut = parser.parsePointcutExpression("execution(* *.bar(..))")
abstract override def invoke(invocation: Invocation): AnyRef =
if (matches(loggingPointcut , invocation)) {
println("=====> Enter: " + invocation.method.getName + " # " + invocation.target.getClass.getName)
val result = super.invoke(invocation)
println("=====> Exit: " + invocation.method.getName + " # " + invocation.target.getClass.getName)
result
} else super.invoke(invocation)
}
}
My factory methods to wire in the interceptors using Java Dynamic Proxy API
package aspect2
import java.lang.reflect.{InvocationHandler, Method, Proxy}
import aspect2.aspect.Invocation
object ManagedComponentFactory {
def createComponent[T](intf: Class[T] forSome {type T}, proxy: ManagedComponentProxy): T =
Proxy.newProxyInstance(
proxy.target.getClass.getClassLoader,
Array(intf),
proxy).asInstanceOf[T]
}
class ManagedComponentProxy(val target: AnyRef) extends InvocationHandler {
override def invoke(proxy: AnyRef, m: Method, args: Array[AnyRef]): AnyRef = invoke(Invocation(m, args, target))
def invoke(invocation: Invocation): AnyRef = invocation.invoke
}
My main function to run the demo:
package aspect2
import aspect2.InterceptorImpl.LoggingInterceptor
import aspect2.aspect.Invocation
object aspectDemo extends App{
var foo = ManagedComponentFactory.createComponent[Foo](
classOf[Foo],
new ManagedComponentProxy(new FooImpl)
with LoggingInterceptor{
override def invoke(invocation:Invocation):AnyRef={
super.invoke(invocation)
}
})
foo.foo("foo")
foo.bar("bar")
}
Foo and Bar are implemented as follows:
trait Foo {
def foo(msg: String)
def bar(msg: String)
}
class FooImpl extends Foo {
val bar: Bar = new BarImpl
def foo(msg: String) = println("msg: " + msg)
def bar(msg: String) = bar.bar(msg)
}
trait Bar {
def bar(msg: String)
}
class BarImpl extends Bar {
def bar(msg: String) = println("msg: " + msg)
}
My results show that my pointcut ("execution(* *.bar(..))") gets both foo.bar("bar") and foo.foo("foo")
The problem was in the line invocation.target.getClass.getDeclaredMethods.exists(pointcut.matchesMethodExecution(_).alwaysMatches) in the object InterceptorImpl which matches all methods of the class. Since both methods foo and bar belong to trait Foo which is extended by class FooImpl the pointcutExpression matches logging happens for both methods.
I'd like to be able to exhaustively match on the types for implementations of a sealed trait, something like in the example below. Ideally I'd like to avoid using reflection (TypeTags) and implicit conversions if possible. Is there any way to exhaustively match on the type of a sealed trait?
object DataTypes {
sealed trait StrFy {
def stringify: String
}
final case class StrFyString(s: String) extends StrFy {
def stringify = s
}
final case class StrFyInt(i: Int) extends StrFy {
def stringify = i.toString
}
def stringifyThings[T <: StrFy](values: T*): String = {
val label = T match {
case StrFyString => "string"
case StrFyInt => "integer"
// cases that don't extend StrFy cause a compile error
}
"The " + label + " values are: " + values.map(_.stringify.fold("")(_+", "+_))
}
def printStringified(): Unit = {
println(stringifyThings(StrFyString("foo"), StrFyString("bar"))) // should print: "the string values are: foo, bar"
println(stringifyThings(StrFyInt(1), StrFyInt(2), StrFyInt(3))) // should print: "the integer values are: 1, 2, 3"
}
}
There is no way to get a value given a type unless you use a typeclass which you ruled out by "no implicits". Therefore, you need to match against an instance.
object DataTypes extends App {
sealed trait StrFy {
def stringify: String
}
final case class StrFyString(s: String) extends StrFy {
def stringify = s
}
final case class StrFyInt(i: Int) extends StrFy {
def stringify = i.toString
}
def stringifyThings[T <: StrFy](values: T*): String = {
def label(value: T) = value match {
case _:StrFyString => "string"
case _:StrFyInt => "integer"
// cases that don't extend StrFy cause a compile error
}
// Will throw if values is empty
"The " + label(values.head) + " values are: " + values.map(_.stringify).mkString(", ")
}
def printStringified(): Unit = {
println(stringifyThings(StrFyString("foo"), StrFyString("bar"))) // should print: "the string values are: foo, bar"
println(stringifyThings(StrFyInt(1), StrFyInt(2), StrFyInt(3))) // should print: "the integer values are: 1, 2, 3"
}
printStringified()
}
Implicits are not that scary though :) have a look:
object DataTypes extends App {
sealed trait StrFy[T] {
def stringify(value: T): String
def label: String
}
implicit object StrFyString extends StrFy[String] {
override def stringify(value: String): String = value
override def label: String = "string"
}
implicit object StrFyInt extends StrFy[Int] {
override def stringify(value: Int): String = value.toString
override def label: String = "integer"
}
def stringifyThings[T: StrFy](values: T*): String = {
val strFy = implicitly[StrFy[T]]
// Safe even on empty values
"The " + strFy.label + " values are: " + values.map(strFy.stringify).mkString(", ")
}
def printStringified(): Unit = {
println(stringifyThings("foo", "bar")) // should print: "the string values are: foo, bar"
println(stringifyThings(1, 2, 3)) // should print: "the integer values are: 1, 2, 3"
}
printStringified()
}
The compiler does the "pattern matching" for you, providing the correct instance given the input type.
You see that typeclasses allow you to get a value, here label, given only a type. This is a quite fundamental concept that makes typeclass-like polymorphism stronger than the subtyping one :)
Let us define simplest inheritance:
class A { val base: String = { println("a hello"); "hello" } }
class B extends A { override val base: String = { println("b hi"); "B hello"} }
Now let's try it out:
scala> new B().base
a hello
b hi
res0: String = B hello
So .. many (/most?) of you out there will likely not be surprised by this. I was . It had been my (incorrect ..) assumption that the B.base would completely override A.base . Instead we see both* methods being invoked : the base method A.base then the overriding method B.base.
Ok then .. so is it true that in order to avoid this doubling-up behavior that the val must be converted to a def (method) ?
class A { def base: String = { println("a hello"); "hello" } }
class B extends A { override def base: String = { println("b hi"); "B hello"} }
And now we have the desired behavior:
scala> new B().base
b hi
res1: String = B hello
Given this situation: when is overriding val useful within the body of a class?
It had been my (incorrect ..) assumption that the B.base would completely override A.base . Instead we see both methods being invoked : the base method A.base then the overriding method B.base.
No, we don't. The println calls in both A.base and B.base happen in the constructor, not when you access base. This is just what (non-lazy) member val means: the right-hand side is executed in the constructor and the result (just "hello" or "B hello" in your case) is stored into a field. Accessing base just returns the value of that field.
And superclass constructor is always fully executed before the subclass constructor, there's no way to override it (you can override any methods it invokes, but you need to be careful so they don't rely on subclass' state which isn't initialized yet).
scala> val b = new B()
a hello
b hi
scala> b.base
res0: String = B hello
when is overriding val useful within the body of a class?
When you want the subclass' val to have a different value, of course.
LAZY val
It came to me ..
class A { lazy val base: String = { println("a hello"); "hello" } }
class B extends A { override lazy val base: String = { println("b hi"); "B hello"} }
And now we get expected behavior:
scala> new B().base
b hi
res1: String = B hello
Suppose I have some abstract value field defined in a trait:
trait Base {
val toBeOverride: String
}
case class Impl(other:Int) extends Base {
override val toBeOverride = "some value"
}
How can I write a function that I can easily get a cloned instance only overriding the toBeOverride value, like this:
// copy only available to case class instance
// v does not have method 'copy'
def overrideBaseValue[T <: Base](v: Base) =
v.copy(toBeOverride = "prefix" + v.toBeOverride)
?
Edit
#som-snytt, I don't think this is a duplicate, just like a Trait is not the same as an Abstract Class. And the answers of that question do not satisfy me, see below.
#Blaisorblade, yes, it is a problem. For instances of each sub case class, the toBeOverride field are the same, so it should not appear in the constructor.
For now all the suggestions are to define an customized copy method in each(!) sub case class and that in my opinion is ugly and shows the incapability of the language.
The simplest solution is to just add the method you want to Base:
trait Base {
val toBeOverride: String
def copyBase(newToBeOverridden: String): Base
}
case class Impl(other:Int, override val toBeOverride: String = "some value") extends Base {
def copyBase(newToBeOverridden: String) = copy(toBeOverride = newToBeOverridden)
}
This also allows to directly create an instance of Impl while specifying the value of toBeOverride (which wasn't possible). The only disadvantage is that now pattern matches using Impl have to change syntax - please update your question and add a comment if that's a problem.
BTW, if you just want to add a prefix (as in your example), that's no problem:
case class Impl(other:Int, override val toBeOverride: String = "some value") extends Base {
def copyBase(newToBeOverridden: String) = copy(toBeOverride = toBeOverride + newToBeOverridden)
}
Here are two mechanisms.
Apparently, in the near future you'll be able to write a macro that can emit the anonymous subclass, but until then, I think this typeclass is not arduous.
Just kicking the tires on Dynamic here.
import scala.language.dynamics
import scala.reflect._
import scala.reflect.runtime.{ currentMirror => cm }
import scala.reflect.runtime.universe._
trait Base {
def m: String
}
case class Impl(p: Int) extends Base {
override val m = "some value"
}
trait Basic extends Dynamic {
protected def m: String
def selectDynamic(f: String): Any =
if ("m" == f) m else reflecting(this, f)
protected def reflecting(b: Basic, f: String) = {
val im = cm.reflect(b)
val member = im.symbol.typeSignature member newTermName(f)
require(member != NoSymbol, s"No such member $f")
(im reflectMethod member.asMethod)()
}
}
case class Implic(p: Int) extends Basic {
override protected val m = "some value"
}
object Test extends App {
implicit class Copy[A <: Base](val b: A) {
def overriding(overm: String): A = (b match {
case impl: Impl => new Impl(impl.p) { override val m = overm }
case b: Base => new Base { override val m = overm }
}).asInstanceOf[A]
}
implicit class Proxy[A <: Basic : ClassTag](val b: A) {
def proximately(overm: String): Basic = new Basic {
override val m = overm
override def selectDynamic(f: String): Any =
if ("m" == f) overm else reflecting(b, f)
override def toString = b.toString
}
}
// asked for this
//def overriding[T <: Base](v: Base) = v.copy(m = "prefix" + v.m)
/* want something like this
def overriding[T <: Base](v: Base) = new Impl(v.p) {
override val m = "some value"
} */
val a = Impl(5)
val b = a overriding "bee good"
Console println s"$a with ${a.m} ~> $b with ${b.m}"
// or
val c = Implic(7)
val d = c proximately "dynomite"
Console println s"$c with ${c.m} ~> $d with ${d.m}"
}
Since traits don't get copy methods automatically, you can try using a Base case class instead:
case class Base(toBeOverride: String)
case class Impl(other: Int, someVal: String = "some value") extends Base(someVal)
def overrideBaseValue[T <: Base](v: Base) =
v.copy(toBeOverride = "prefix" + v.toBeOverride)
The problem that you're going to run into though, is that copy returns an instance of Base and I don't think that you can convert it back to your original Impl class. For instance, this won't compile:
def overrideBaseValue[T <: Base](v: T): T =
v.copy(toBeOverride = "prefix" + v.toBeOverride)
In Scala Objects are singletons... so if I make:
trait SimpleTrait {
def setString(s: String): Unit = {
InnerTraitObject setString s
}
def getString(): String = {
return InnerTraitObject getString
}
object InnerTraitObject {
var str: String = ""
def setString(s: String): Unit = {
str = s
}
def getString(): String = {
return str
}
}
}
Then
class SimpleClass extends SimpleTrait{
/// empty impl
}
and:
object App {
def main(args: Array[String]): Unit = {
val a = new SimpleClass();
val b = new SimpleClass();
a.setString("a");
println("A value is " + a.getString());
println("B value is " + b.getString());
}
}
I would like to see
A value is a
B value is a
but i got
A value is a
B value is
My question is:
If object is singleton then why if i put it into Trait then it behave like common object?
It´s not a global singleton, it´s a singleton referring to the instance of the trait (which can have several instances). It depends where you define the singleton: if defined in a package then it´s singleton concerning the package and the package is singleton, too, so the object is singleton. You see, it depends on the context you are defining something as a singleton. If the context is singleton then the defined object too.