|
Re: Traits in Scala
|
Posted: May 2, 2008 9:49 AM
|
|
> The article was interesting, but I think it proved that > the use of traits didn't simplify that much the addition > of a property listener. > > Wouldn't this have been 'easier' with the use of a AOP, > defining a join point for the set* methods? > That would help with defining the set methods, but wouldn't you still need to write the add/remove property change listener methods? I agree this may not be a very compelling example for traits, but what Ted is showing is that you can mix in the add/remove property change listener methods with the trait. The other design issue I'd raise with Ted's example, though, is that the firePropertyChange method is going to be public in the class that mixes in the BoundPropertyBean trait. It should not be.
The three main things to know about traits is that they are just like classes, except:
1. their constructor can't take parameters 2. although you can only extend one superclass, you can mix in multiple traits 3. the meaning super in a trait is not defined until you mix it into a class
What's delayed is the definition of super . That's how traits differ from traditional multiple inheritance, and that's how they avoid the diamond problem. This is the real difference between Scala's traits and C++'s multiple inheritance, and it makes traits much more useful in comparison.
The type of this is known when you write the trait. By default it is the trait type, but it can be defined to be a different type. If you do that, the trait can only be mixed into a class that when instantiated will be an instance of that named type. That's how you can state a dependency, a type that the trait needs, when you define the trait. So if you say your this is of type Elevator , then that trait can only be mixed into Elevator or one of Elevator's subclasses. I think this may be what Ted is referring to when he says that "you can define methods that aren't checked for correctness until they're incorporated into a trait-using class definition." But really everything is type checked at every compile. So I think "not checked for correctness" is a poor choice of words.
The other thing I'd point out is that Scala has a much more concise way to do accessor methods. Where you'd do this in Java:
//This is Java public class Person { private String lastName; private String firstName; private int age; public Person(String fn, String ln, int a) { lastName = ln; firstName = fn; age = a; } public String getFirstName() { return firstName; } public void setFirstName(String v) { firstName = v; } public String getLastName() { return lastName; } public void setLastName(String v) { lastName = v; } public int getAge() { return age; } public void setAge(int v) { age = v; } }
In Scala, you'd do this:
class Person( var firstName: String, var lastName: String, var age: Int )
That's it. That gives you a constructor that takes those three parameters, private fields to store them, and accessor methods. The accessor methods have names that allow you to use them this way:
val bob = new Person("Bob", "Jones", 29) bob.age = 31 // Be honest, Bob println(bob.age)
It looks like you're accessing a public field, but you're really going through accessor methods. So you could define them yourself if you wanted to add behavior, such as throwing an exception if someone tries to set a negative age or empty or null name. If you need JavaBeans style set and get methods, Scala has a standard annotation you can use to get them:
import scala.reflect.BeanProperty
class Person( @BeanProperty var firstName: String, @BeanProperty var lastName: String, @BeanProperty var age: Int )
You can't call the set and get methods from within Scala, but they are there on the class, so tools like Hibernate, etc., can call them. From Scala, you are forced to use the prettier syntax I showed above, which looks like field accesses.
In Ted's example, though, he wants to fire a property change event anytime the set method is called. So that's why he had to define the set methods explicitly in his example. The set method you get with BeanProperty just sets the field. It won't call firePropertyChange method.
|
|