I want to define multiple properties of different "classes"/prototypes when creating a new object.
class Animal
constructor: (#name, #temperament, #diet) ->
#methods that use those properties
eat: (food) ->
console.log "#{name} eats the #{food}."
class Bird extends Animal
constructor: (#wingSpan) ->
#methods relating only to birds
class Cat extends Animal
constructor: (#tailLength) ->
#methods relating only to cats
myCat = new Cat ("long", {"Mr. Whiskers", "Lazy", "Carnivore"})
I'm doing something wrong, though. Only the constructor for Cat seems to get any properties.
Also, is there a way to define them with key/value pairs?
Ideally, I'd write something like myCat = new Cat (tailLength: "long", name: "Mr. Whiskers", temperament: "Lazy"), so that I can define properties that aren't in order and it would fall back to a default if I failed to define a property like "diet".
My understanding is that prototype methods will bubble up, so if I call myCat.eat 'cat food', the output should be "Mr. Whiskers eats the cat food." But... it can't be since the Animal class isn't getting the new Cat's name.
Just use {} if you mean "an object".
class Animal
constructor: ({#name, #temperament, #diet}) ->
#methods that use those properties
eat: (food) ->
console.log "#{#name} eats the #{food}."
class Bird extends Animal
constructor: ({#wingSpan}) ->
super
#methods relating only to birds
class Cat extends Animal
constructor: ({#tailLength}) ->
super
#methods relating only to cats
myCat = new Cat(tailLength: "long", name: "Mr. Whiskers", temperament: "Lazy", diet: "Carnivore")
Related
Let's say I have the following trait
trait Named {
def name: String
}
and the following Algebraic Data Type
sealed trait Animal extends Named
case object Dog extends Animal {
override val name: String = "dog man"
}
case object Cat extends Animal {
override val name: String = "cat man"
}
case object Owl extends Animal {
override val name: String = "I am an owl left in the dark"
}
Now, I can deserialize an instance of string into my Animal ADT with the following method.
object Animal {
def apply(name: String): Animal = name match {
case Dog.name => Dog
case Cat.name => Cat
}
}
#oxbow_lakes mentions at the end of his answer that:
Can't instantiate easily from persisted value. This is also true but,
except in the case of huge enumerations (for example, all currencies),
this doesn't present a huge overhead.
I find that the fact that when you add a new value it needs to be added to the deserialization code explicitly as error prone (I thought that the compiler would warn me of an in-exhaustive match, but take a look at Owl above and the apply method - there was no warning issued...)
Is there no better way? (If not with the standard scala toolset, a third party one?)
This problem already solved by enumeratum library:
https://github.com/lloydmeta/enumeratum
Your code could be written like this:
import enumeratum._
import enumeratum.EnumEntry.Lowercase
sealed trait Animal extends EnumEntry with Lowercase
object Animal extends Enum[Animal] {
val values = findValues
case object Dog extends Animal
case object Cat extends Animal
case object Owl extends Animal
}
val dogName = Animal.Dog.entryName
val dog = Animal.withNameInsensitive(dogName)
One thing you could try is to use reflection to obtain the set of types that extend Animal, then use that to create a Map[String,Animal] using name to lookup object values, then use the map in your Animal.apply function.
Refer to this question for more information on obtaining the Animal subclasses.
What are the differences among these ways of defining Animal:
First way:
trait Animal {
def color: String
}
Second way:
trait Animal {
val color: String
}
Third way:
abstract class Animal(color: String) {}
Dog is a subclass of Animal. Consider the first way and the second way of defining Animal, what are the differences among the following ways of defining Dog:
First way:
case class Dog() extends Animal {
override def color:String = "black"
}
Second way:
case class Dog() extends Animal {
val color = "black"
}
Third way:
case class Dog(color: String) extends Animal {}
Forth way:
case class Dog(override val color: String) extends Animal(color) {}
Whoa, a lot to be answered here.
Regarding your first question, if you use a val then all subclasses must also use val. If you use def, subclasses can implement it either using def, val or lazy val. If color is a stable, immutable value, then declaring it as "val" in the trait makes sense since it imposes that all implementations in concrete subclasses will also be immutable.
The third way makes color only available in the constructor body and not visible from the outside. However, if you wrote
abstract class Animal(val color: String) {}
then it would be the same as the second way, only using abstract class instead of the trait. You could create a new animal and access its color attribute.
Regarding dog, defining color as def means that it will be computed every time it is invoked (i.e. when someone tries to access myDog.color). Defining it as val means that it will be an immutable value calculated once and for all when dog object is created. If it were a lazy val, then it would be calculated once and for all, but not when the dog is created, but when its color attribute is invoked (the calculation is postponed until the point of usage, hence the "lazy").
As I said above, if the Animal trait uses a val, then the Dog must also use a val. If Animal uses a def, then Dog can implement that as a def, val or lazy val.
Third way of writing a Dog is simply providing a parameter in case of writing an Animal with a class parameter (which was also third way in animal case). As I said earlier, in this case you cannot access the color attribute from the outside (that is, have val myDog = new Dog("blue") and access myDog.color).
Fourth way of writing a dog is implementing the Animal in case it was written in the way I have shown you above in the code (with using a val keyword). Now the color attribute will be visible. Override is not mandatory since you are implementing an abstract method, not overriding a concrete method, but you can leave it if you like (this way compiler will warn you if you, say, misspell "color" or someone removes the color from Animal class).
Perhaps this article can help too.
I have the following code:
class Animal
constructor: (#name) ->
say: () -> console.log "Hello from animal called #{ #name }"
class Dog extends Animal
say: () ->
super.say()
console.log "Hello from dog called #{ #name }"
a = new Animal('Bobby')
a.say()
d = new Dog("Duffy")
d.say()
The result is not
Hello from animal called Bobby
Hello from animal called Duffy
Hello from dog called Duffy
But I get the following error:
Hello from animal called Bobby
Hello from animal called Duffy
Uncaught TypeError: Cannot call method 'say' of undefined
How come super is undefined? How to call a parent method in order to extend it? Thanks
I found the answer myself, it should be:
class Dog extends Animal
say: () ->
super
console.log "Hello from dog called #{ #name }"
I have a class like this:
class Cow
#feet : 4
constructor: (#name) ->
bes = new Cow "Bessie"
The question is, is it possible to access feet only given bes?
You can use the JavaScript constructor property to get at the class and there you will find your feet:
class Cow
#feet: 4
constructor: (#name) ->
class HexaCow extends Cow
#feet: 6
bes = new Cow('Bessie')
pan = new HexaCow('Pancakes')
alert(bes.constructor.feet) # 4
alert(pan.constructor.feet) # 6
Demo: http://jsfiddle.net/ambiguous/ZfsqP/
I don't know of any special CoffeeScript replacement for constructor though.
How can I find that class of an object once it has been instantiated?
class Cat
constructor: (#name) ->
class Dog
constructor: (#name) ->
cat = new Cat "Kitty"
dog = new Dog "Doggy"
if (cat == Cat) <- I want to do something like this
Just change the == to instanceof
if(cat instanceof Cat)
If you wanted to know the type name of a particular object (which is what I was just looking for when I found this question), you can use the syntax {object}.constructor.name
for example
class Cat
constructor: (#name) ->
class Dog
constructor: (#name) ->
cat = new Cat()
dog = new Dog()
console.log cat.constructor.name
console.log dog.constructor.name
which will output
Cat
Dog
The way to do this is to check the type of an object using either
instanceof
or
typeof
i.e.
if (obj instanceof Awesomeness){
//doSomethingCrazy();
}
Just as in JavaScript, Coffee Script does not provide any abstraction over these functions
AFAIU, the general solution would be using #constructor - Useful when you don't know or don't want to specify the class name.
There was even a discussion regarding making ## a shortcut for it.