This annotation can be added on a trait to declare the list of types that a class implementing that trait is supposed to extend. This is useful when you want to be able to call methods from the class implementing the trait without having to declare all of them as members of the trait. Self types are particularily useful in combination with CompileStatic, if you know that a trait can only be applied to a specific type but that the trait cannot extend that type itself. For example, imagine the following code:
class Component { void methodInComponent() }
trait ComponentDecorator {
void logAndCall() {
println "Calling method in component"
methodInComponent()
}
// other useful methods
}
class DecoratedComponent extends Component implements ComponentDecorator {}
This will work because the trait uses the dynamic backend, so there is no check at
compile time that the methodInComponent call in logAndCall is actually
defined. If you annotate the trait with CompileStatic, compilation
will fail because the trait does not define the method. To declare that the trait can be
applied on something that will extend Component, you need to add the SelfType
annotation like this:
class Component { void methodInComponent() }
@CompileStatic
@SelfType(Component)
trait ComponentDecorator {
void logAndCall() {
println "Calling method in component"
methodInComponent()
}
// other useful methods
}
class DecoratedComponent extends Component implements ComponentDecorator {}
This pattern can therefore be used to avoid explicit casts everywhere you need to call a method
that you know is defined in the class that will implement the trait but normally don't have access
to, which is often the case where a trait needs to be applied on a class provided by a third-party
library.