an applied case for red ninjas
One of my favorite things about scala are case classes. Those coming from a java background might be used to writing code like this.
public class Ninja {
private int experience;
private List<Weapon> weapons;
private String color;
public Ninja(int experience, List<Weapon> weapons, String color) {
this.experience = experience;
this.weapons = weapons;
this.color = color;
}
public int getExperience() {
return experience;
}
public String getColor() {
return color;
}
public List<Weapon> getWeapons() {
return weapons;
}
}
This is commonly known as the bean style of class design. For now, I’ll leave out the mutating setABC(D d) methods that make beans mutable. Code formatting may vary, but for this example, let’s call that 23 lines of code (counting spaces) to define a type with three properties.
Scala has a built-in, more simplified, idiom for this style of class design called case classes. To define the same class in scala we would write
case class Nina(experience: Int, weapons: List[Weapon], color: String)
Again, formatting may vary, but for this example let’s call that one line of code. This bit of syntactic nicety also provides us with a complementary apply method that takes as its arguments the same as the constructor’s so that we can create our classes sans the new keyword by applying the arguments to our Ninja class with:
Ninja(10, List(new Sword), "red")
Building an Army
Now lets say you want to build an army of red ninjas. You might find it more convenient to write an auxiliary constructor in the java class such as
public Ninja(int experience, List<Weapon> weapons) {
this(experience, weapons, "red");
}
Now you can build your army with:
List<Ninja> redArmy = new ArrayList<Ninja>(){{
add(new Ninja(50, Arrays.asList(new Sword())));
add(new Ninja(60, Arrays.asList(new Bow())));
}};
Scala too, has its own way of declaring auxiliary constructors by adding methods to classes in the form
def this(experience: Int, weapons: List[Weapon]) = this(experience, weapons, "red")
Case Confusion
Recently, I ran into a gothca with auxiliary constructors in case classes.
Say we add an auxiliary constructor to the Scala version of Ninja as:
case class Ninja(experience: Int, weapons: List[Weapon], color: String) {
def this(experience: Int, weapons: List[Weapon]) = this(experience, weapons, "red")
}
When you invoke Ninja(10, List(new Sword())) you will get
error: wrong number of arguments for method apply: (Int,List[Weapon],String)Ninja in object Ninja
wtf?
What you are actually invoking there is Scala’s implicit apply method with two arguments.
Ninja.apply(10, List(new Sword()))
If you’ve been paying attention the case class has not generated that for you. Okay, so let’s go ahead and add one to our Ninja case class.
def apply(experience: Int, weapons: List[Weapon]) = new Ninja(experience, weapons)
This time when you invoke Ninja(10, List(new Sword())) you should see the error:
error: wrong number of arguments for method apply: (Int,List[Weapon],String)Ninja in object Ninja
A Friend of a Companion
Turns out the convenience of case classes also adds one restriction to defining them: case classes can only have one apply method which is defined by the primary constructor’s arguments.
Great. So now what? Oh yea, classes can have friends too. They are called companion objects.
We can work around this constraint a companion object.
object Ninja {
/** I can haz called the auxiliary constructor of the case class Ninja */
def apply(experience: Int, weapons: List[Weapon]) = new Ninja(experience, weapons)
}
Ninja(10, List(new Bow())) // -> I win!
Now we’re are ready to fight.
Ninja(50, List(new Sword())) :: Ninja(60, List(new Bow())) :: Nil
Have you any other suggestions?