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 particularly 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.