The thing is, is there any way (That does not involve reflective black magic), to implicitly override a method, when two known traits are implemented?
Think that we have a class SysImpl that implements two mixins: System and Container:
// Where system acts as the interface for another consumer
trait System {
def isEmpty = false
}
trait Container[A] {
val elements = mutable.Set[A]()
}
// Then, we typically implement:
class SysImpl extends System with Container[Int] {
// We override the default system implementation here
override def isEmpty = elements.isEmpty
}
Just as an example.
Is there any way of implementing either a third trait, or making something to the original, that makes the implementation implicitly override the isEmpty method, in case that System & Container[A] are present?
The first thing that comes to my mind is creating an extension method, but that would be shadowing at its best (Wouldn't it?). I need the method to be overridden properly, as the call is dispatched by a consumer who only sees Systems.
(Example, omitting details)
class AImpl extends System with Container[A]
object Consumer {
def consume(sys: System) = if (sys.isEmpty) { /* Do things */ }
}
// Somewhere...
object Main {
def main() = {
Consumer.consume(new AImpl)
}
}
Just
trait Mix[A] extends Container[A] with System {
override def isEmpty = elements.isEmpty
}
Related
I want to do something like this:
interface Serializable<FromType, ToType> {
fun serialize(): ToType
companion object {
abstract fun deserialize(serialized: ToType): FromType
}
}
or even this would work for me:
interface Serializable<ToType> {
fun serialize(): ToType
constructor(serialized: ToType)
}
but neither compiles. Is there a syntax for this, or will I be forced to use make this an interface for a factory? Or is there another answer? đŽ That'd be neat!
Basically, nothing in a companion object can be abstract or open (and thus be overridden), and there's no way to require the implementations' companion objects to have a method or to define/require a constructor in an interface.
A possible solution for you is to separate these two functions into two interfaces:
interface Serializable<ToType> {
fun serialize(): ToType
}
interface Deserializer<FromType, ToType> {
fun deserialize(serialized: ToType): FromType
}
This way, you will be able to implement the first interface in a class and make its companion object implement the other one:
class C: Serializable<String> {
override fun serialize(): String = "..."
companion object : Deserializer<C, String> {
override fun deserialize(serialized: String): C = C()
}
}
Also, there's a severe limitation that only a single generic specialization of a type can be used as a supertype, so this model of serializing through the interface implementation may turn out not scalable enough, not allowing multiple implementations with different ToTypes.
For future uses, it's also possible to give the child class to a function as a receiver parameter:
val encodableClass = EncodableClass("Some Value")
//The encode function is accessed like a member function on an instance
val stringRepresentation = encodableClass.encode()
//The decode function is accessed statically
val decodedClass = EncodableClass.decode(stringRepresentation)
interface Encodable<T> {
fun T.encode(): String
fun decode(stringRepresentation: String): T
}
class EncodableClass(private val someValue: String) {
// This is the remaining awkwardness,
// you have to give the containing class as a Type Parameter
// to its own Companion Object
companion object : Encodable<EncodableClass> {
override fun EncodableClass.encode(): String {
//You can access (private) fields here
return "This is a string representation of the class with value: $someValue"
}
override fun decode(stringRepresentation: String): EncodableClass {
return EncodableClass(stringRepresentation)
}
}
}
//You also have to import the encode function separately:
// import codingProtocol.EncodableClass.Companion.encode
This is the more optimal use case for me. Instead of one function in the instanced object and the other in the companion object like your example, we move both functions to the companion object and extend the instance.
I have an architectural problem, more precisely, a suboptimal situation.
For an adaptable test environment, there is a context that is updated by a range of definition methods, which each define different entities, i.e. alter the context. For simplicity, the definitions here will just be integers, and the context a growing Seq[Int].
trait Abstract_Test_Environment {
def definition(d: Int): Unit
/* Make definitions: */
definition(1)
definition(2)
definition(3)
}
This idea is now implemented by a consecutively altered âvarâ holding the current context:
trait Less_Abstract_Test_Environment extends Abstract_Test_Environment {
/* Implement the definition framework: */
var context: Context = initial_context
val initial_context: Context
override def definition(d: Int) = context = context :+ d
}
Since the context must be set before âdefinitionâ is applied, it cannot be set by variable assignment in the concluding class:
class Concrete_Test_Environment extends Less_Abstract_Test_Environment {
context = Seq.empty
}
An intermediate âinitial_contextâ is required but a plain overriding does not do the job either:
class Concrete_Test_Environment extends Less_Abstract_Test_Environment {
override val initial_context = Seq.empty
}
The only viable solution seems to be an early initialization, which most likely is the purpose this feature has been created for:
class Concrete_Test_Environment extends {
override val initial_context = Seq.empty
} with Less_Abstract_Test_Environment
HOWEVER, our setting still fails because when âdefinitionâ is applied in âAbstract_Test_Environmentâ, the VAR âcontextâ in âLess_Abstract_Test_Environmentâ is still not bound, i.e. null. Whereas the def âdefinitionâ is âinitialized on demandâ in âLess_Abstract_Test_Environmentâ (from âAbstract_Test_Environmentâ), the var âcontextâ is not.
The âsolutionâ I came up with is merging âAbstract_Test_Environmentâ and âLess_Abstract_Test_Environmentâ. This is not what I wanted since it destroys the natural separation of interface/goal and implementation, which has been realized by the two traits.
Do you see any better solution? I am sure Scala can do better.
Simple solution: Do not initialize your object during its creation, except you are in the bottom level class. Instead, add an init method, which contains all of the initialization code and then call it either in the most bottom level class (which is safe, since all parent classes have already been created) or wherever the object is created.
Side effect of the whole thing is that you can even override the initialization code in a subclass.
One possibility is to make your intermediate trait a class:
abstract class Less_Abstract_Test_Environment(var context: Context = Seq.empty) extends Abstract_Test_Environment {
override def definition(d: Int) = context = context :+ d
}
You can now subclass it, and pass different initial contexts in as parameters to constructor.
You can do this at the "concrete" level too, if you'd rather have the intermediate as a trait:
trait Less_Abstract_Test_Environment extends Abstract_Test_Environment {
var context: Context
override def definition(d: Int) = context = context :+ d
}
class Concrete_Test_Environment(override var context: Context = Seq.empty) extends Less_Abstract_Test_Environment
What would be even better though is using functional approach: context should be a val, and definion should take the previous value, and return the new one:
trait Abstract {
type Context
def initialContext: Context
val context: Context = Range(1, 4)
.foldLeft(initialContext) { case (c, n) => definition(c, n) }
def definition(context: Context, n: Int): Context
}
trait LessAbstract extends Abstract {
override type Context = Seq[Int]
override def definition(context: Context, n: Int) = context :+ n
}
class Concrete extends LessAbstract {
override def initialContext = Seq(0)
}
You can employ the idea of a whiteboard, which contains only data, which is shared by a number of traits which contain only logic (not data!). See below some untested code off the cuff:
trait WhiteBoard {
var counter: Int = 0
}
trait Display {
var counter: Int
def show: Unit = println(counter)
}
trait Increment {
var counter: Int
def inc: Unit = { counter = counter + 1 }
}
Then you write unit tests like this:
val o = new Object with Whiteboard with Display with Increment
o.show
o.inc
o.show
Doing this way, you separate definition of the data from places where the data is required, which basically means that you can potentially mix in traits in any order. The only requirement is that the whiteboard (which defines data) is the first trait mixed in.
trait UserRepository {
def findByFirstName(firstName: String): Seq[User]
}
trait UserBusinessDelegate extends UserRepository {
abstract override def findByFirstName(firstName: String) = {
super.findByFirstName(firstName)
}
}
class MockUserRepository extends UserRepository {
override def findByFirstName(firstName: String) = {
// whatever
}
}
val userRepository = new MockUserRepository with UserBusinessDelegate
userRepository.findByFirstName("John") // OK
However, if I change UserBusinessDelegate as follows:
trait UserBusinessDelegate {
self: UserRepository =>
override def findByFirstName(firstName: String): Seq[User] = {
self.findByFirstName(firstName) // requires explicit return type, thinks this is a recursive call
}
}
val userRepository = new MockUserRepository with UserBusinessDelegate
userRepository.findByFirstName("John") // StackOverflow!!!
I understand stackable pattern and hence how the first case works. My question is why the 2nd doesn't.
In the second snippet you have a recursive call without an exit condition:
override def findByFirstName(firstName: String): Seq[User] = {
self.findByFirstName(firstName)
}
This will always call findByFirstName from UserBusinessDelegate (because you're using self, which basically says that this object will have this kind of behaviour at runtime, not that it's parent will have it and therefore we should call parent's method) creating a new stack frame with each call -> stack overflow.
In the second snippet UserBusinessDelegate's findByFirstName will be called and then you call MockUserRepository's method from it using super -> no recursion -> no stack overflow. You can check Scala's stackable trait pattern for more info.
#Edit: to make it more clear, in the snippet that throws a SO exception the findByFirstName method from MockUserRepository won't be called because you are overriding it in UserBusinessDelegate therefore the anonymous class created with new MockUserRepository with UserBusinessDelegate will only contain the overriden method and that's why the SO, is that clear?
Why would you assume that the method from MockUserRepository would get invoked?
#Edit2: the code doesn't compile without override because self: UserRepository => tells the compiler that a method with such a signature will already be there at runtime and you cannot have 2 methods with the same signature. The first example works only because it's a stackable trait, such traits are dynamically bound and can modify the behaviour but have to call super at some point (which normally isn't allowed without the abstract override modifier, I really recommend going through the link I posted about stackable pattern).
Maybe someone else knows a way, from what I know there's no way to call the mock method unless you change the method name in UserBusinessDelegate and drop the override, then you can call self.findByFirstName and it will call the method from MockUserRepository.
Could someone explain why scala would allow a public variable, to satisfy the implementation of an abstract declared Protected item? My first assumption is that the compiler would complain, but I created a small test to see if this worked, and to my surprise it does. Is there an advantage to this? (perhaps this is normal in OOP?) Any methods to avoid the accidental pitfall?
object NameConflict extends App {
abstract class A {
protected[this] var name:String
def speak = println(name)
}
class B(var name:String) extends A { //notice we've declared a public var
}
val t = new B("Tim")
t.speak
println(t.name) // name is exposed now?
}
It's normal and as in Java. Sometimes it's desirable to increase the visibility of a member.
You can't do it the other way around and turn down visibility in a subclass, because the member can by definition be accessed through the supertype.
If invoking a method has terrible consequences, keep the method private and use a template method that can be overridden; the default implementation would invoke the dangerous method.
abstract class A {
private[this] def dangerous = ???
final protected def process: Int = {
dangerous
template
}
protected def template: Int = ???
}
class B extends A {
override def template = 5
}
I need a smart mechanism for component composition which allows mixed in traits to initialize after the composed component. The following throws a NullPointerException:
class Component {
def addListener(pf: PartialFunction[Any, Unit]) {}
}
trait DynamicComponent {
protected def component: Component
component.addListener {
case x =>
}
}
class Foo extends DynamicComponent {
protected val component = new Component
}
new Foo // -> NullPointerException
The following things are not options for me:
Using protected lazy val component; that would produce an avalange of dozens of vals needing to become lazy, something I do not want.
Putting addListener in a method, e.g. initDynamic(); because I will be mixing in many traits, and I don't want to remember to call half a dozen initFoo() methods.
Using DelayedInit. This doesn't work with traits, at least according to the scaladocs.
I could live with a single init() call, but only under the following conditions:
all mixed in traits can easily declare to be invoked in this one single call
it is a compile error to forget the init() statement.
You can delay the initialization of a trait by by using early definitions. (See section 5.1.6 of the scala language specification)
class Foo extends {
protected val component = new Component
} with DynamicComponent
It's even clunkier than your solution, but you can always require the creation of a val that must be set with the init() method. You could choose to not do it last and get an error at runtime, but at least you won't forget it entirely:
class Component {
def addListener(pf: PartialFunction[Any, Unit]) {
println("Added")
}
}
trait Dyn {
protected def component: Component
protected val initialized: Init
class Init private () {}
private object Init { def apply() = new Init() }
def init() = { component.addListener{ case x => }; Init() }
}
class Foo extends Dyn {
protected val component = new Component
protected val initialized = init()
}
No cheating!:
> class Bar extends Dyn { protected val component = new Component }
<console>:12: error: class Bar needs to be abstract, since value
initialized in trait Dyn of type Bar.this.Init is not defined
class Bar extends Dyn { protected val component = new Component }
The advantage this has is if you need multiple things to be in place before you initialize all of them cooperatively, or if your Component class is final so you can't mix in anything else.
AN idea could be to use the trick described here:
Cake pattern: how to get all objects of type UserService provided by components
All your components that should be initialized could be registered in some Seq[InitializableComponent]. And then you could initialize all registered components with a foreach.
No component will be forgotten in that Seq because they are registered automatically, but you can still forget to call the foreach anyway...
Here is one idea (I am happy to read about other suggestions):
class Component {
def addListener(pf: PartialFunction[Any, Unit]) {
println("Added")
}
}
trait DynamicComponentHost {
protected def component: Component with DynamicPeer
protected trait DynamicPeer {
_: Component =>
addListener {
case x =>
}
}
}
class Foo extends DynamicComponentHost {
protected val component = new Component with DynamicPeer
}
new Foo
So basically I am forcing the component to mix in a type that can only be provided by the mixed in trait. Reasonable? Looks a bit too complicated in my eyes.