I need to change an object's behavior on run-time.
I have two concrete object (singleton) classes Dog and Cat, both extend from an abstract class Animal.
The object animal of Animal type should be changed to either Dog or Cat on runtime.
Here's the rough code of something that I am trying to accomplish:
abstract class Animal() {
def sound: Unit
}
object Cat extends Animal {
def sound: Unit = {
print("meow")
}
}
object Dog extends Animal {
def sound: Unit = {
print("bark")
}
}
object MyProgram {
val animal: Animal = _
def initialize(config: Config): Unit ={
// check the config
if (config.getString("ANIMAL_TYPE").equals("Dog")) {
animal = Dog
} else {
animal = Cat
}
}
def run: Unit = {
animal.sound
}
}
def main(): Unit = {
val config = ConfigFactory.praseFile(myconfigfile)
MyProgram.initialize(config)
MyProgram.run
}
Can something like this be done in Scala? If not, how can I accomplish this in Scala.
The error you are getting has nothing to do with typing or dynamic polymorphism:
var animal : Animal = _
^
On line 19: error: local variables must be initialized
As you can see, there is nothing wrong with the types in your program, you simply need to properly initialize your variable:
def main() : Unit = {
var animal = if(config.getString("ANIMAL_TYPE").equals("Dog")) {
Dog
} else {
Cat
}
// this should print either bark or meow
animal.sound
}
Note that there are a couple of non-idiomatic things in your code. A more idiomatic version would look something like this:
trait Animal {
val sound: String
}
object Cat extends Animal {
override val sound = "meow"
}
object Dog extends Animal {
override val sound = "bark"
}
def main(): Unit = {
val animal = if (config.getString("ANIMAL_TYPE").equals("Dog")) Dog else Cat
// this should print either bark or meow
print(animal.sound)
}
Use a trait instead of an abstract class.
Don't use an empty constructor / initializer, just use no constructor / initializer.
Separate input/output from computation: the printing should be done in the main method, not in the Animal.
Use the explicit override modifier.
Don't use type annotations if the type is obvious.
Don't use curly braces around single expressions.
Don't use var, always use val.
if is an expression, don't ignore its return value.
You are defining the main function incorrectly.
The main function should be a member of some object, and it should have an argument of the type Array[String].
object SomeRandomName {
def main(): Unit = {
var animal: Animal = null
// check the config
if(config.getString("ANIMAL_TYPE").equals("Dog")) {
animal = Dog
} else {
animal = Cat
}
// this should print either bark or meow
animal.sound
}
}
And this code should compile and run well.
SIDENOTE: If you are using Scala 3 (instead of Scala 2.x), then you don't need to make a wrapper object of the main function. The function can be at the top level.
Your code should work almost as it is, just assign something to animal.
object MyProgram {
//First of all use some default value for animal.
//It will be safer to use.
var animal: Animal = Cat
// or use explicitly null if there is no sensible default value.
// underscore makes it less clear what will happen.
//var animal: Animal = null
//I thing match is better than if/else here especially when third animal will arrive. You can assign it like that:
def initialize(config: Config): Unit ={
animal = (config.getString("ANIMAL_TYPE") match {
case "Dog" => Dog
case _ => Cat
}
}
}
Related
I'm wondering if its possible to have Enumerations with one value parametrized.
Something like:
object Animal extends Enumeration {
val Dog = Value("dog")
val Cat = Value("cat")
val Other = Value(?)
def create(value: String): Animal.Value = {
Animal.values.find(_.toString == value).getOrElse(Other(value))
}
}
And, for use, something like:
create("dog") // will return Animal.Dog
create("giraffe") // will return Animal.Other
create("dog").toString // will return "dog"
create("giraffe").toString // will return "giraffe"
That is, to be able to have some values typed, but to leave one free.
Thanks!!!
Lucas.
I have to apologize for jumping the gun. I was thinking in Java terms, where an enum is a very rigid thing. Scala, however, is a bit more flexible in that regard. Enumeration does not stop us from extending the enumeration class ourselves.
Disclaimer: This is probably not a good idea. It works, but I don't know how it will behave with respect to serialization or the other nice properties that ordinary enumerations have. So if it works for you, great! But I can't promise that it's a good solution.
object Animal extends Enumeration {
val Dog = Value("dog")
val Cat = Value("cat")
def create(value: String): Animal.Value = {
Animal.values.find(_.toString == value).getOrElse(OtherAnimal(value))
}
}
// Extending the enumeration class by hand and giving it a `String` argument.
case class OtherAnimal(name: String) extends Animal.Value {
override def id = -1
override def toString = name
}
println(Animal.create("dog").toString) // dog
println(Animal.create("cat").toString) // cat
println(Animal.create("giraffe").toString) // giraffe
I am relatively new to scala so please bear me if I asked silly questions.
I have a requirement where I need to invoke a method run time.
I have a trait which is being extended by two classes
trait Animal {
def walk():DataFrame
}
This is extended by two classes.
class Dog(sparkSession: SparkSession) extends Animal {
def walk():DataFrame = {
.............
}
}
class Cat(sparkSession: SparkSession) extends Animal {
def walk():DataFrame = {
.............
}
}
Now from a config file I will get a list of these class names
Lets say like this
val animals = ["com.xy.Dog","com.xy.Cat"]
I need to invoke these classes and execute walk methods.
Can I do something like this?
animals.forEach{ animalString =>
val animalObject = Class.forName(animalString ).newInstance().asInstanceOf(Animal)
animalObject.walk
}
There are a few issues, let's take them one by one:
To build a list in scala you need to do:
val animals = List("com.xy.Dog", "com.xy.Cat")
The forEach method is actually foreach, so there's a small typo. Finally, when you call the newInstance you should get the appropriate constructor before that, otherwise it will use the default one.
animals.foreach { animalString =>
val animalObject = Class.forName(animalString)
.getConstructor(classOf[DataFrame]) // Get the constructor for a DataFrame argument
.newInstance(dataframe) // Pass the dataframe instance
.asInstanceOf[Animal]
animalObject.walk
}
I've made a couple of small changes to the code for you to see it working. You can run the app to see the output:
class Dog extends Animal {
def walk(): Unit = { println("I'm a dog.") }
}
class Cat extends Animal {
def walk(): Unit = { println("I'm a cat.") }
}
object AnimalTest extends App {
val animals = List("com.xy.Dog", "com.xy.Cat")
animals.foreach { animalString =>
val animalObject = Class.forName(animalString)
.newInstance()
.asInstanceOf[Animal]
animalObject.walk
}
}
Note that I've removed the constructor arguments here to easily build instances. The rest is about the same. I hope this helps you.
I have a Scala application, where pretty much every object extends a specific trait, which holds all the main functions and variables used by pretty much the entire system.
I want to add a --testing flag to my app's command line variables, which will shift the the results of some of the functions in the trait.
Putting it simply, I'd like the variable accepted in the main to have an affect that alters something in the trait before it is extended by the objects - without sending it explicitly to all objects.
Any ideas how that can be performed?
I doubt you really want to dynamically modify a trait, and I am not sure if it possible that all your classes inheriting that trait would be affected. I don't know enough about the compiler and byte code.
A way to accomplish something similar would be to have your trait take a parameter, and make your trait act conditionally on the parameter.
trait Foo {
val testing: Boolean
def fn1(): Unit = {
if (testing) {
println("testing")
} else {
println("production")
}
}
}
class Bar(val testing: Boolean) extends Foo {
def fn2(): Unit = {
fn1()
}
}
new Bar(true).fn2()
new Bar(false).fn2()
Your question is broad and this is just my 5 cents.
Update
trait Foo {
def fn1(): Unit = {
if (Foo.testing) {
println("testing")
} else {
println("production")
}
}
}
object Foo {
var testing: Boolean = false
}
class Bar extends Foo {
def fn2(): Unit = {
fn1()
}
}
object SOApp extends App {
new Bar().fn2()
Foo.testing = true
new Bar().fn2()
}
Consider passing the 'testing' flag to the trait's initializer block like this:
trait MyTrait {
var testMode: Boolean = _
def doSomething(): Unit = {
if (testMode)
println("In Test Mode")
else
println("In Standard Mode")
}
}
// IMPORTANT: Your best bet would be to create some Config object
// that is loaded and initialized in a main method.
// Define test-specific Config class:
case class Config(testMode: Boolean) {
def isTestMode: Boolean = this.testMode
}
// Instantiate in main method:
val config = new Config(true)
// Later, extend the trait:
class MyObj extends MyTrait { testMode = config.isTestMode() }
// Then just invoke
new MyObject().doSomething()
In below code I receive this error :
class Animal needs to be abstract, since: it has 5 unimplemented members. /** As seen from class Animal, the
missing signatures are as follows. * For convenience, these are usable as stub implementations. */ def
favFood_=(x$1: Double): Unit = ??? def flyingType_=(x$1: scala.designpatterns.Strategy.Flys): Unit = ??? def
name_=(x$1: String): Unit = ??? def sound_=(x$1: String): Unit = ??? def speed_=(x$1: Double): Unit = ???
If I initialize all of the instance variables of class Animal to _ then the code compiles correctly. What does these error mean ?
package scala.designpatterns
/**
*
* Decoupling
* Encapsulating the concept or behaviour that varies, in this case the ability to fly
*
* Composition
* Instead of inheriting an ability through inheritence the class is composed with objects with the right abilit built in
* Composition allows to change the capabilites of objects at runtime
*/
object Strategy {
def main(args: Array[String]) {
var sparky = new Dog
var tweety = new Bird
println("Dog : " + sparky.tryToFly)
println("Bird : " + tweety.tryToFly)
}
trait Flys {
def fly: String
}
class ItFlys extends Flys {
def fly: String = {
"Flying High"
}
}
class CantFly extends Flys {
def fly: String = {
"I can't fly"
}
}
class Animal {
var name: String
var sound: String
var speed: Double
var favFood: Double
var flyingType: Flys
def tryToFly: String = {
this.flyingType.fly
}
def setFlyingAbility(newFlyType: Flys) = {
flyingType = newFlyType
}
def setSound(newSound: String) = {
sound = newSound
}
def setSpeed(newSpeed: Double) = {
speed = newSpeed
}
}
class Dog extends Animal {
def digHole = {
println("Dug a hole!")
}
setSound("Bark")
//Sets the fly trait polymorphically
flyingType = new CantFly
}
class Bird extends Animal {
setSound("Tweet")
//Sets the fly trait polymorphically
flyingType = new ItFlys
}
}
You must initialize variables. If you don't, Scala assumes you're writing an abstract class and a subclass will fill in the initialization. (The compiler will tell you so if you have just a single uninitialized variable.)
Writing = _ makes Scala fill in the default values.
The point is to make you think about what happens when someone (e.g. you, after you forget that you need to set things first) calls something that uses e.g. the sound without it having been set.
(In general you should at least think carefully about whether this is the right way to structure your code; many fields that require initialization before usage is safe, without any mechanism to enforce initialization, is asking for problems.)
I want to make the following example so that Collar is immutable
trait Collar{
var text:String="";
}
class dog(val name:String){
def bark()= ...
}
val snoopy = new dog("snoopy") with Collar;
snoopy.text="charley's dog";
println(snoopy.text)
However when I try something like
trait Collar(val text:String){
}
I get the compile time error
traits or objects may not have parameters
Is there currently a a way to do this? If not, why not?
Remember, a good dog shouldn't care when it has a Collar.
Not at all sure what your real question is, but perhaps this is what you're looking for:
trait Collar{
val tagText: String // 'val', not 'var'; left undefined here
}
class Dog(val name:String) {
def bark()= ...
}
val snoopy = new Dog("snoopy") with Collar {
val tagText= "charley's dog"
}
println(snoopy.tagText)
Alternatively, Collar.tagText can be set with an initial/default value AND also be overridden where Dog is instantiated.