I need only one instance of a class, so I have to use an object instead of a class. I also need to set some initial value chosen by a client, so I need to a constructor for an object, something like this:
object Object1(val initValue: Int){
//.....
}
I can't use this exact code in Scala. How do I deal with that then?
You have a couple of choices:
Make it a class, have the client construct it, give the value in the parameter
Pro: Preserves immutability
Con: Having only a single instance might be hard to manage
Add a variable for the param to the object, add a setter.
Pro: You still have a singleton
Con: There is mutable state now
Implement a multiton
Pro: Gives you (apparent) immutability and singleton (per param)
Con: More code to implement
You could implement a multiton like this in scala:
class Object1 private (val initValue: Int) {
// ...
}
object Object1 {
val insts = mutable.Map.empty[Int, Object1]
def apply(initV: Int) =
insts.getOrElseUpdate(initV, new Object1(initV))
}
UPDATE You could also turn this into a "singleton with parameter":
object Object1 {
var inst: Option[(Int, Object1)] = None
def apply(initV: Int) = inst match {
case Some((`initV`, i)) => i
case Some(_) =>
sys.error("Object1 already instantiated with different param")
case None =>
val i = new Object1(initV)
inst = Some((initV, i))
i
}
}
The object isn't created until you reference it, so you could do something like the following:
object Test1 extends App {
var x = Console.readLine
println(Object1.initVal)
}
object Object1 {
val initVal:String = Test1.x
}
Related
I have a class
class MyClass {
def apply(myRDD: RDD[String]) {
val rdd2 = myRDD.map(myString => {
// do String manipulation
}
}
}
object MyClass {
}
Since I have a block of code performing one task (the area that says "do String manipulation"), I thought I should break it out into its own method. Since the method is not changing the state of the class, I thought I should make it a static method.
How do I do that?
I thought that you can just pop a method inside the companion object and it would be available as a static class, like this:
object MyClass {
def doStringManipulation(myString: String) = {
// do String manipulation
}
}
but when I try val rdd2 = myRDD.map(myString => { doStringManipulation(myString)}), scala doesn't recognize the method and it forces me to do MyClass.doStringManipulation(myString) in order to call it.
What am I doing wrong?
In Scala there are no static methods: all methods are defined over an object, be it an instance of a class or a singleton, as the one you defined in your question.
As you correctly pointed out, by having a class and an object named in the same way in the same compilation unit you make the object a companion of the class, which means that the two have access to each others' private fields and methods, but this does not mean they are available without specifying which object you are accessing.
What you want to do is either using the long form as mentioned (MyClass.doStringManipulation(myString)) or, if you think it makes sense, you can just import the method in the class' scope, as follows:
import MyClass.doStringManipulation
class MyClass {
def apply(myRDD: RDD[String]): Unit = {
val rdd2 = myRDD.map(doStringManipulation)
}
}
object MyClass {
private def doStringManipulation(myString: String): String = {
???
}
}
As a side note, for the MyClass.apply method, you used the a notation which is going to disappear in the future:
// this is a shorthand for a method that returns `Unit` but is going to disappear
def method(parameter: Type) {
// does things
}
// this means the same, but it's going to stay
// the `=` is enough, even without the explicit return type
// unless, that is, you want to force the method to discard the last value and return `Unit`
def method(parameter: Type): Unit = {
// does things
}
You should follow scala's advice.
val rdd2 = myRDD.map(MyClass.doStringManipulation)
Write this inside the class then it will work as expected.
import MyClass._
I want to have a collection of objects, each object a companion of a different class, which classes all share a common method defined in a superclass that can be invoked when looping through the collection with a foreach(). I want the constructors of these sibling-classes to have the same named parameters and default parameter values as each other. Finally, I want to minimize repeated code.
Thus far, I am trying to do this with case classes, since--if it worked--it would eliminate all the duplicated code of the companion-objects for each type. The problem is that if I put all these companion objects into a Set, when I take them out again I lose the default parameters and parameter names.
Here is some example code of what I am describing:
trait MyType {
val param: String
def label = param // all instances of all subclasses have this method
}
case class caseOne(override val param: String = "default") extends MyType
case class caseTwo(override val param: String = "default") extends MyType
object Main extends App {
// I can construct instances using the companion objects' `apply()` method:
val works1 = caseOne(param = "I have been explicitly set").label
// I can construct instances that have the default parameter value
val works2 = caseOne().label
// But what I want to do is something like this:
val set = Set(caseOne, caseTwo)
for {
companion <- set
} {
val fail1 = companion() // Fails to compile--not enough arguments
val fail2 = companion(param = "not default") // Fails also as param has lost its name
val succeeds = companion("nameless param") // this works but not what I want
println(fail1.label + fail2.label) // this line is my goal
}
}
Notably if the Set has only one element, then it compiles, suggesting the inferred type of the multi-element Set lacks the parameter name--even though they are the same--and the default values. Also suggesting that if I gave the Set the right type parameter this could work. But what would that type be? Not MyType since that is the type of the companion classes rather that the objects in the Set.
I could define the companion objects explicitly, but that is the repeated code I want to avoid.
How can I loop through my collection, constructing instances of MyType subclasses on each iteration, with constructors that have my desired parameter names and default values? All while minimizing repeated code?
Update: Originally the example code showed caseOne and caseTwo as having different default values for param. That was incorrect; they are now the same.
You're not going to be able to get exactly what you want since you don't really have much control over the auto-generated companion objects. In particular for this to work they would all need to extend a common trait. This is why it fails to compile when the set has more than one companion object; even though they all have a method with the same signature, they don't extend a common trait for the compiler to utilize.
You can use a nested case class and get something very similar though:
trait MyType {
val param: String
def label = param // all instances of all subclasses have this method
}
abstract class MyTypeHelper(default: String) {
case class Case(param: String) extends MyType
def apply(param: String) : Case = Case(param)
def apply(): Case = apply(default)
}
object One extends MyTypeHelper("default one")
object Two extends MyTypeHelper("default two")
object Example {
val works1 = One(param = "I have been explicitly set").label
val works2 = One().label
val set = Set(One, Two)
for {
companion <- set
} {
val a = companion()
val b = companion(param = "not default")
val c = companion("nameless param")
println(a.label + b.label)
}
}
Instead of having a caseOne type, you have One.Case, but it still implements MyType so you shouldn't have any issue anywhere else in the code that uses that trait.
Is it possible to add a member variable to a class from outside the class? (Or mimic this behavior?)
Here's an example of what I'm trying to do. I already use an implicit conversion to add additional functions to RDD, so I added a variable to ExtendedRDDFunctions. I'm guessing this doesn't work because the variable is lost after the conversion in a rdd.setMember(string) call.
Is there any way to get this kind of functionality? Is this the wrong approach?
implicit def toExtendedRDDFunctions(rdd: RDD[Map[String, String]]): ExtendedRDDFunctions = {
new ExtendedRDDFunctions(rdd)
}
class ExtendedRDDFunctions(rdd: RDD[Map[String, String]]) extends Logging with Serializable {
var member: Option[String] = None
def getMember(): String = {
if (member.isDefined) {
return member.get
} else {
return ""
}
}
def setMember(field: String): Unit = {
member = Some(field)
}
def queryForResult(query: String): String = {
// Uses member here
}
}
EDIT:
I am using these functions as follows: I first call rdd.setMember("state"), then rdd.queryForResult(expression).
Because the implicit conversion is applied each time you invoke a method defined in ExtendedRDDFunctions, there is a new instance of ExtendedRDDFunctions created for every call to setMember and queryForResult. Those instances do not share any member variables.
You have basically two options:
Maintain a Map[RDD, String] in ExtendedRDDFunctions's companion object which you use to assign the member value to an RDD in setMember. This is the evil option as you introduce global state and open pitfalls for a whole range of errors.
Create a wrapper class that contains your member value and is returned by the setMember method:
case class RDDWithMember(rdd: RDD[Map[String, String]], member: String) extends RDD[Map[String, String]] {
def queryForResult(query: String): String = {
// Uses member here
}
// methods of the RDD interface, just delegate to rdd
}
implicit class ExtendedRDDFunctions(rdd: RDD[Map[String, String]]) {
def setMember(field: String): RDDWithMember = {
RDDWithMember(rdd, field)
}
}
Beside the omitted global state, this approach is also more type safe because you cannot call queryForResult on instances that do not have a member. The only downsides are that you have to delegate all members of RDD and that queryForResult is not defined on RDD itself.
The first issue can probably be addressed with some macro magic (search for "delegate" or "proxy" and "macro").
The later issue can be resolved by defining an additional extension method in ExtendedRDDFunctions that checks if the RDD is a RDDWithMember:
implicit class ExtendedRDDFunctions(rdd: RDD[Map[String, String]]) {
def setMember(field: String): RDDWithMember = // ...
def queryForResult(query: String): Option[String] = rdd match {
case wm: RDDWithMember => Some(wm.queryForResult(query))
case _ => None
}
}
import ExtendedRDDFunctions._
will import all attributes and functions from Companion object to be used in the body of your class.
For your usage look for delagate pattern.
I was wondering if there is a shorter version of
var longVariableName: MyType = MyTyp("Value")
longVariableName = longVariableName.addSomething("added")
case class MyType(value: String) {
def addSomething(add: String): MyType = ???
}
Maybe something like
var longVariableName: MyType = MyType("Value")
longVariableName = _.addSomething("extended")
Would be so nice :)
Thank you
I guess the easiest way would be:
val longVariableName = MyTyp("Value")
.addSomething("added")
.addSomethingElse("other")
.addSomeMore("stuff")
As long as each method returns the base type (i.e. "Builder" pattern), you can further chain calls.
In this way, you use a value (not a variable) and given that each method call returns a new instance of the case class, it's immutable, with no side-effect.
Furthermore, case classes support a builder-like pattern with the copy method, which allows to "add" information down the road in an immutable way.
Something like this:
case class Player(name:String, rank:Option[String] = None) {
def withRank(rank:Int)= this.copy(rank=Some(s"top${100-rank}%"))
}
val player = Player("sparky").withRank(5)
(Or multi-line)
val player = Player("sparky")
.withRank(5)
You can define a + method for your case class:
case class MyType(value: String) {
def +(add: String) = MyType(value + add)
}
var longVariableName: MyType = MyType("Value")
longVariableName += "extended"
You can use the copy method without defining anything else.
case class MyType(value: String, otherValue: String, otherOthervalue: String)
val longName = MyType("Value","OtherValue","OtherOtherValue")
val longerName = longName.copy(value=longName.value+"extended")
i need help with this code.
object test {
var list : Vector[MyType] = null
}
object foo extends MyType { // Mytype is a trait
println("TEST ")
test.list.:+(foo)
def myfunc() { //need to define this as this is there in the trait
// i do some operations
}
}
object Bar extends MyType { // Mytype is a trait
println("TEST ")
test.list.:+(Bar)
def myfunc(){
// i do some operations
}
}
now i want to go through the list and call myfunc() for all the objects that are extending MyType.
test.list foreach( t2 => t2.myfunc() )
the value's are not getting added to the list. Can someone let me know what i am doing wrong. Its not working. Is there a way to get that print statement working?
Your problem is, that the object is not constructed as a class, so that the code is called automatically. You could do two things. Either you extend App and call main or you write a function.
trait X
object test {
var list = Vector.empty[X]
}
object Foo extends App with X {
test.list :+= Foo
override def toString() = "Foo"
}
object Bar extends X {
def add() {
test.list :+= Bar
}
override def toString() = "Bar"
}
Foo.main(null)
Bar.add()
test.list foreach println
This code prints:
Foo
Bar
Extending App only adds a main methode to an object, containing all the code in the object.
You need to initialize test with an empty Vector rather than null. The way to do that in Scala is to use the factory method from the Vector object, and let type-inference do its job. For example:
var list = Vector.empty[MyType]
As you get the practice of doing that, you'll find yourself more focused on creating the data than on declaring its type, which in this case would have resolve this error before it happened.
Next the operation
test.list.:+(foo)
will not update test.list because, since Vector is immmutable, this method just returns a new updated copy and cannot affect the reference of list.
Try instead
test.list = test.list.:+(foo)
// or (with more idiomatic operator notation)
test.list = test.list :+ foo
// or (using syntactic sugar)
test.list :+= foo