I have noticed trait definitions like this
trait SomeTrait{ self =>
def isDefinedAt(x: String): Boolean
}
What does this “self” exactly do? Any documentation about it?
I have noticed trait definitions like this
trait SomeTrait{ self =>
def isDefinedAt(x: String): Boolean
}
What does this “self” exactly do? Any documentation about it?
You can read about it here: https://docs.scala-lang.org/tour/self-types.html
When it is used without a type annotation like in your example it’s basically just defining an alias for this
. A good use case is when you are working with inner classes:
class Foo { outer =>
class Bar {
def getFoo: Foo = outer
}
}
Jasper’s answer is spot on for the example you are asking about. It is interesting to note that while these are called self-types, They don’t have to use the name self, as Jasper’s example showed. When I saw the title of the question and not the full text, a different situation popped into my mind, so I’m going to mention that here as well. If you start using Akka, the name self is used to refer to the ActorRef of an Actor. You will see that usage far more in Scala code using Actors than you likely will see self-types in normal Scala code.
That’s one use, which avoid the Foo.this
you’d have to use in Java. The other use is to force a type on the class the trait is mixed in (hence the name self type, I suppose):
trait SomeTrait { self: SomeType =>
...
}
This makes SomeTrait
usable only in classes that have type (or subtype) SomeType
. You can use more complex variations, e.g., to require the using class to have two types:
trait SomeTrait { self: SomeType with SomeOtherType =>
...
}
Does this mean that only SomeType
can extend trait SomeTrait
?
No – it means that anything extending SomeTrait
must also extend SomeType
.
At least any concrete type that you can instantiate must include SomeType in its hierarchy.
SomeType
or a subtype of SomeType
. It lets you create dependencies among modules. For instance:
class System extends Base with CompA with CompB with CompC
If CompA
needs to use something from CompC
, it can be defined as:
trait CompA { self: CompC => ... }
In this case:
class System extends Base with CompA with CompB
cannot be compiled because System
does not have type CompC
.
If CompA
needs to use something from CompB
and CompC
, it can be defined as:
trait CompA { self: CompB with CompC => ... }
If CompA
needs so much that it only makes sense in this particular system, it can be written as:
trait CompA { self: Base => ... }
In this example, CompA
, CompB
and CompC
are traits; Base
can be a trait or a class.