Is it possible to have constructor parameters that don't become fields? For example,
class Foo(p1: String, p2: Int) {
val f1 = p1 * p2
}
The only field I want is f1, not p1 and p2. The idea is to derive fields from parameters.
This is good as it is - both p1 and p2 aren't fields here, just f1. Making them fields is opt-in rather than opt-out (except for case classes, where all parameters are also fields, and you can't do anything about it).
To make them fields, you'd have to add var or val, something like
class Foo(val p1: String, var p2: Int)
EDIT:
In case you want a field with the same name as a parameter but a different type or something, you can do something like this:
class Foo(p1: String, val p2: Int) {
//Can be val if you want
var _p1: Int = (p1 : String).toInt
def p1: Int = _p1
def p1_=(p1: Int) = _p1 = p1 //An optional setter
}
This is practically the same as having a field called p1.
If you also want to set fields using some complex operations and use local variables, you can use blocks (and maybe an auxiliary constructor)
class Foo(p1: String, val p2: Int) {
val _p1: Int = {
val intermediateVariable: String = p1
intermediateVariable.toInt
}
def p1: Int = _p1
or
class Foo(val p1: Int) {
def this(p1: String) = this({
val uselessIntermediateVariable = p1.toInt
uselessIntermediateVariable
})
}
In a simple class, if you don't add any modifier as val or var, the parameters of the constructor are private elements. Example:
class C(val a: Int, b: Int) {
val c = a + b
}
val i = new C(1,2)
println(i.a) // it's public
println(i.b) // this line won't allow you to compile, it's private
println(i.c) // also public
The exception is made is you make a case class, with case modifier all the parameters of the constructor will be public. You can make them private marking as private the parameter. Example:
case class C(a: Int, private val b: Int) {
val c = a + b
}
val i = new C(1,2)
println(i.a) // it's public
println(i.b) // this line won't allow you to compile, it's private
println(i.c) // also public
Just to clarify the existing answers, non-case class parameters can automatically become fields, but only if they are used in methods or lazy val initializers, e.g.
class Foo(p1: String, p2: Int) {
val f1 = p1 * p2
def m1() = p1 + p1
}
will make p1 a field. There's no way to avoid it other than not using them in such a way.
Related
I would like to mark the default method generated by scala compiler to be ignored from being serialized.
#JsonIgnore def getSum(a: Int, b: Int = 2): Int = a + b
compiler creates two methods:
def getSum(a: Int, b: Int): Int = a.+(b);
<synthetic> def getSum$default$2(): Int = 2; (default value at position 2)
The first method getSum does not get serialized as the JsonIgnore is applied on it, but the new default parm method generated is serialized by jackson, how to tell Jackson to not serialize it?
Example:
import com.fasterxml.jackson.annotation.JsonIgnore
import com.fasterxml.jackson.databind.ObjectMapper;
case class A(id: Int) {
#JsonIgnore def getSum(a: Int, b: Int = 2): Int = a + b
}
#Test
def test(): Unit = {
val a = A(5)
val mapper = new ObjectMapper()
println(mapper.writeValueAsString(a))
}
output: {"id":5,"sum$default$2":2}
I believe this is a bug, but there are workarounds:
Explicitly ignore the generated property
The #JsonIgnoreProperties annotation can be used at the class level.
#JsonIgnoreProperties(Array("sum$default$2"))
case class A(id: Int) {
def getSum(a: Int, b: Int = 2): Int = a + b
}
Rename the method
It's detected as a property because it matches the JavaBeans getter convention. If the method name can be changed to remove the get prefix, it won't be treated as a property by Jackson:
case class A(id: Int) {
def calculateSum(a: Int, b: Int = 2): Int = a + b
}
Avoid default arguments
Of course, another option is to avoid the use of default arguments entirely in favour of method overloading:
case class A(id: Int) {
def getSum(a: Int, b: Int): Int = a + b
def getSum(a: Int): Int = getSum(a, 2)
}
These examples are all demonstrated in this Scastie: https://scastie.scala-lang.org/TimMoore/werEmJF9S7ip4QP5sT73CQ/39
All of these options produce this JSON:
{"id":5}
Let's say I have the following hierarchy:
abstract class A(val x: Int, val y: String)
class B(override val x: Int, override val y: String, val z: Int) extends A(x,y)
Now I want to initialize the values from a configuration object but I want the actual values to be the original ones.
If I would do the configuration in B only then I would do something like:
class B(override val x: Int, override val y: String, val z: Int) extends A(x,y)
def this(conf: Conf) {
this(conf.get("x"), conf.get("y"), conf.get("z"))
}
but I want to be able to do the same in A.
If I add:
abstract class A(val x: Int, val y: String)
this(conf: Conf) {
this(conf.get("x"), conf.get("y))
}
I wouldn't be able to define B (I don't have the conf in the B default constructor).
EDIT:
To make this clearer:
The use case I have is a factory which generates the proper B (there are a large number of child classes). It does so by doing something like:
def getElement(elemType: String, conf: Conf): A = {
elemType match {
case "B" => new B(conf)
}
}
Currently, I have a companion object:
object B {
def apply(conf: conf) = B(conf.getx(), conf.gety(), ...)
}
The problem is that when I need to add a new element to the parent A, I need to go and change every one of the children and I have the same code conf.getx(), conf.gety() etc.
Ideally I would like B constructor to be able to do something like:
class B(conf: Conf) extends A(conf)
but I can't do this as this would make conf into a member of B.
You can also use companion objects to define alternative constructors:
case object A {
def apply(conf: Conf): A = new A(conf.get("x"), conf.get("y"))
}
case object B {
def apply(conf: Conf): B = new B(conf.get("x"), conf.get("y"), conf.get("z"))
}
After looking around some more I found this (also points to this) and this. All three basically say the following:
If we use an argument without val or var and it is only referenced in the constructor then it does NOT become a member.
This means the following solution would work:
abstract class A(conf: Conf) {
val x = conf.getX()
val y = conf.getY()
}
class B(conf: Conf) extends A(conf) {
val z = conf.getZ()
}
would provide the required behavior cleanly and simply.
I created a class A that extends class B, which takes two parameters.
When I create a class object of A using reflection and passing two parameters, an object is create w/o any exception, but the two "constructor" parameters do not contain the values I passed.
Is that the intended behaviour or am I doing something wrong?
Sample code:
class B (p1: Int = 0, p2: String = "") {
...
}
class A extends B {
...
}
val mirror = universe.runtimeMirror(getClass.getClassLoader)
val classSymbol = mirror.classSymbol(Class.forName("package.A"))
val constructor = mirror.reflectClass(classSymbol).reflectConstructor(
classSymbol.toType.decl(universe.termNames.CONSTRUCTOR).asMethod)
val object: B = constructor(1, "C").asInstanceOf[B]
"object" contains an instance of A, but with p1 = 0 and p2 = "". I expected it to contain p1 = 1 and p2 = "C".
If I move (p1: Int = 0, p2: String = "") to A it works as expected.
Is that the normal behaviour and my expectations were just wrong, or is there a mistake in my code?
Let's first take reflection out of the picture completely, and just try to do what you are doing without using reflection:
class B(p1: Int = 0, p2: String = "")
class A extends B
//val b = (new A(1, "C")).asInstanceOf[B]
// even simpler:
val b = new A(1, "C")
// error: too many arguments for constructor A: ()A
// new A(1, "C")
// ^
As you can see, it doesn't even compile. Why? Well, you defined A's constructor to take no arguments! Somehow, during the reflection process, this information gets lost and you end with a legal call passing arguments to a constructor that doesn't take any and just ignores them.
I don't know the underlying reason, but it probably has to do with some quirk in how reflection works at runtime.
In any case this kind of code will get you what you want (without repeating the default values specified in the superclass). Note that you should explicitly specify the parameters from the superclass you are using in the subclass:
class B (p1: Int = 0, p2: String = "") {
override def toString: String = s"p1 = $p1, p2 = $p2"
}
class A (p1: Int, p2: String) extends B (p1, p2) { }
...
val obj: B = constructor(1, "C").asInstanceOf[B]
println(obj)
Output:
p1 = 1, p2 = C
Let us assume we have a trait T. What is the best way to achieve the following:
Everybody who writes an implementation of T should be forced to provide a possibility that allows a parameter-free initialization of T, i.e., we probably have to enforce the implementation of a configurable factory.
All logic/data that only depends on the actual initialization parameters (of a certain implementation A of T) should be handled/stored centrally, but should be available in both the factory and A.
The most simple/concise way I see to achieve this (approximately) would be to add a trait for a factory and link T to this factory:
trait T {
val factory: TFactory
}
trait TFactory {
def build(): T
val description: String // example for logic/data that only depend on the parameters
}
// example implementation:
class A(val factory: AFactory, paramA: Int, paramB: Int, paramC: Int) extends T
class AFactory(paramA: Int, paramB: Int, paramC: Int) extends TFactory {
def build = new A(this, paramA, paramB, paramC)
val description = f"$paramA $paramB $paramC"
}
Obviously this does not really "enforce" the implementation of a factory (as long as there is an alternative implementation available) and obviously it is possible to generate instantiations of A which link to a "wrong" TFactory. What I also don't like about this approach is the repetition of the initialization parameters. I often create yet another class AParams which again wraps all parameters (for instance to facilitate adding new parameters). Thus, I end up with three classes, which imho is a lot of boilerplate for this simple problem.
My question is whether there is a (maybe completely) different approach, which achieves the same primary goals but is more concise?
I'm not quite sure I get the full intent of your requirements but what do you think of this behavior?
trait TFactory{
def build():T
val description:String
}
trait T extends TFactory
//can't declare A without build and not make it abstract
class A(paramA: Int, paramB: Int, paramC: Int) extends T {
def build = new A(paramA, paramB, paramC)
val description = f"$paramA $paramB $paramC"
}
val a1 = new A(1, 4, 5)
val a2 = a1.build()
//We can give ourselves as a factory to something that expects TFactory
val factory:TFactory = a1
val a_new = factory.build()
//More likely we can just give our build method
def func(f: ()=>T) = {
val new_t = f()
new_t
}
val a_newer = func(a1.build)
println(a1 +": " + a1.description)
println(a2 +": " + a2.description)
println(a_new +": " + a_new.description)
println(a_newer +": " + a_newer.description)
Output:
Main$$anon$1$A#69267649: 1 4 5
Main$$anon$1$A#69b1fbf4: 1 4 5
Main$$anon$1$A#24148662: 1 4 5
Main$$anon$1$A#3f829e6f: 1 4 5
Add a representation type parameter:
trait Factory[Prod] {
def build(): Prod
}
trait Prod[Repr] {
def factory: Factory[Repr]
}
Or, if you want to "enforce" that the type remains the same (I wouldn't do that unless you gain something from it):
trait Prod[Repr <: Prod[Repr]] {
def factory: Factory[Repr]
}
Then:
case class AConfig(a: Int, b: Int)
case class A(config: AConfig) extends Prod[A] {
def factory = AFactory(config)
}
case class AFactory(config: AConfig) extends Factory[A] {
def build() = A(config)
}
val f0 = AFactory(AConfig(1, 2))
val p0 = f0.build()
val f1 = p0.factory
val p1 = f1.build()
assert(p0 == p1)
I'm trying to define a class which will have as a field a Set, and would like to be able to manipulate this set directly from the container class:
case class MyClass(prop: String) extends TraversableLike[Int,MyClass] {
private def mySet: Set[Int]() = Set()
override def foreach[U](f: Int => U) = data.foreach[U](f)
override def newBuilder: Builder[Int, MyClass] =
new ArrayBuffer[Int] mapResult (a => MyClass(prop, a.toSet))
implicit def canBuildFrom: CanBuildFrom[MyClass, Int, MyClass] =
new CanBuildFrom[MyClass, Int, MyClass] {
def apply(): Builder[Int, MyClass] = newBuilder
def apply(from: MyClass): Builder[Int, MyClass] = newBuilder
}
}
I'd like to be able to do
var obj = MyClass("hello")
obj += 1
obj = obj map (_+1)
The first instruction (obj+= 1) works, but the second doesn't.
The problem is that I can't put my implicit canBuildFrom into an object MyClass because builder needs informations dependant of the instance (in this case, the prop field).
Is there a solution make my implicit accessible and keep its instance dependance ?
I'd like to avoid making my class mutable.
There are a couple of problems with your code:
Set[Int]() is not a valid type for mySet, you should drop the ()
member mySet should be a val, not a def
you are calling apply() methods that don't exist
if you want to hook in into the collections hierarchy at the Traversable level, you don't get methods like + and the derived +=. If you're representing a set, then it should be a Set.
Here's a revised attempt:
import mutable.Builder
import generic.CanBuildFrom
class MyClass private (val prop: String, private val mySet: Set[Int] = Set())
extends immutable.Set[Int] with SetLike[Int, MyClass] {
def -(elem: Int) = MyClass(prop, mySet - elem)
def +(elem: Int) = MyClass(prop, mySet + elem)
def contains(elem: Int) = mySet.contains(elem)
def iterator = mySet.iterator
override def empty: MyClass = MyClass(prop)
override def stringPrefix = "MyClass(" + prop + ")"
}
object MyClass {
def DefaultProp = "DefaultProp"
def apply(prop: String, mySet: Set[Int] = Set()) = new MyClass(prop, mySet)
def newBuilder(prop: String = DefaultProp): Builder[Int, MyClass] =
Set.newBuilder[Int] mapResult (set => MyClass(prop, set))
implicit def canBuildFrom: CanBuildFrom[MyClass, Int, MyClass] =
new CanBuildFrom[MyClass, Int, MyClass] {
def apply(): Builder[Int, MyClass] = newBuilder()
def apply(from: MyClass): Builder[Int, MyClass] = newBuilder(from.prop)
}
}
Then you can write:
var obj = MyClass("hello")
obj += 1
println(obj) // prints MyClass(hello)(1)
obj = obj map (_ + 1)
println(obj) // prints MyClass(hello)(2)
Let's dissect that:
MyClass is now explicitly an immutable set with a custom representation declared in the type arguments to SetLike. prop is a public val member; the actual set, mySet, is a private val.
Then we need to implement the four operations on which Set relies, by simply forwarding them the mySet. (This looks a like it could be factored out. For the Seqs, there is a class SeqForwarder that does a similar job; I couldn't find a SetForwarder, though). Finally, we provide an empty method, on which the built-in inherited builder also relies. Finally, overriding stringPrefix enables a nicer string representation with "MyClass" and the value of prop.
Note that The canBuildFrom object MyClass calls newBuilder, passing the prop of the original collection when it can. This means that most of the time, you can keep this value while mapping, etc. over MyClass instances. We need to define a default value for prop, however, since CanBuildFroms must define an apply method that does not tell what the originating collection is. (Question: why would this actually happen?)
Finally, our implementation of newBuilder does not rely on ArrayBuffers any more, but directly builds the Set instance that will be wrapped by your new MyClass instance.
Some more resources:
http://www.scala-lang.org/docu/files/collections-api/collections-impl.html
Implement a scala collection so that map, filter, etc. produce the right type