The code below is a simplified example of what I'm trying to achieve. I have a container class Properties that holds at most instance of many different subclasses of Property. I have generic add and get methods that work well to set and retrieve these properties.
However, if a property class does not exist in the Properties object, I want to be able to return a new instance which is created simply by calling the constructor of that class with its default parameters. See the comment below in the getOrDefault method for an idea of what i'm trying.
EDIT
I can do this if I require a zero-argument constructor as suggested in the comments, although I'd like to avoid this if possible.
import scala.reflect.ClassTag
trait Property {}
object Properties {
type PropertyTag = ClassTag[_ <: Property]
type PropertyMap = Map[PropertyTag, Property]
}
import Properties._
final case class Properties(
var properties: PropertyMap = Map.empty[PropertyTag, Property]
) {
def add[S <: Property](prop: S)(implicit tag: ClassTag[S]): Unit = {
properties += (tag -> prop)
}
def get[S <: Property](implicit tag: ClassTag[S]): S = {
properties(tag).asInstanceOf[S]
}
def getOrDefault[S <: Property](implicit tag: ClassTag[S]): S = {
if (properties.contains(tag)) {
properties(tag).asInstanceOf[S]
} else {
// This does not work since the TestProperty2 has parameters
tag.runtimeClass.newInstance.asInstanceOf[S]
}
}
}
final case class TestProperty() extends Property {}
final case class TestProperty2(val x) extends Property {
def this() = this(3)
}
val properties = Properties()
properties.add(TestProperty())
val prop = properties.get[TestProperty]
val prop2 = properties.getOrDefault[TestProperty2]
println(prop2)
Related
I am new to scala and thus my question might be due to a lack of understanding of abtract types and traits.
I currently have a Sensor trait which is generic and defines a value and newValue method.
trait Sensor[T] {
def value: T
def newValue(): Unit = {}
}
One concrete implementation of Sensor is MemorySensor, which just uses a variable to store the value and has a set value method which sets the value and fires the newValue method.
class MemorySensor[T] extends Sensor[T] {
var internalValue: T = null.asInstanceOf[T]
def setValue(newVal: T): Unit = {
internalValue = newVal
newValue()
}
def value: T = internalValue
}
There is also an AbstractSO (SO = subject + Observer) class which uses abstract types to implement Subject/Observer pairs.
class AbstractSO {
type O <: AbstractObserver
type S <: AbstractSubject
trait AbstractSubject {
this: S =>
def register(observer: O) = {
//we could use mutable collection here too
observers = observer :: observers
}
var observers: List[O] = List[O]()
def notifyObservers() = {
observers.foreach(o => o.notifyObserver(this))
}
}
trait AbstractObserver {
def notifyObserver(subject: S)
}
}
One example of a concrete Subject/Observer is the ActionSO
object ActionSO extends AbstractSO {
type S = ActionSubject
type O = ActionObserver
trait ActionSubject extends AbstractSubject {
def action() = {
notifyObservers()
}
}
trait ActionObserver extends AbstractObserver {
override def notifyObserver(actionSubject: ActionSubject) = {
println("action observer called")
}
}
}
Now I want to implement a concrete Subject/Observer-Pair for Sensors with the requirement that the SensorSubject should be a mixin trait for sensors.
So I guess the target would be to use the SensorSubject like this:
val x = new MemorySensor[Int] with SensorSubject[Int]
However whatever I try to implement the SensorSO, I always get either some kind of "illegal inheritance" error or "self-type does not conform to..".
As far as I know this cannot be done without creating an extra class that extends from AbstractSO, but uses generic types. (but I don´t know how this helps me to achieve my target anyways)
It would be very nice if someone could help me out!
EDIT:
As SergGr wanted to see my SensorSubject (which is what I don´t know how to implement, I will post one of my various tries)
Note however that this does NOT COMPILE
object SensorSO extends AbstractSensorSO {
//TODO: i shouldn´t use "Any" here - this won´t work
override type S = SensorSubject[Any]
trait SensorSubject[T] extends AbstractSensorSubject with Sensor[T] {
this: S => //this generates problems as well
}
}
Here is my AbstractSensorSO
class AbstractSensorSO extends AbstractSO {
type S <: AbstractSensorSubject
type O <: AbstractSensorObserver
trait AbstractSensorSubject extends AbstractSubject {
this: S =>
}
trait AbstractSensorObserver extends AbstractObserver {
}
}
As you can see the AbstractSensorSO basically doesn´t do anything, I just added it because it was mentioned in a hint to the solution that one needs an subclass of AbstractSO, before creating the concrete SensorSO object.
One problem I am facing is that the Sensor trait is generic so for the SensorSubject to use the Sensor trait AFAIK i have to make the SensorSubject generic too.
Normally this wouldn´t be a problem, but as i use abstract types I would have to define the "S" type in the SensorSO with generics too (e.g.
type S = SensorSubject[T]
But as the generic type "T" is not known in that context it obviously gives an error (as the generic "T" is only available the context of the generic trait SensorSubject)
If I try to drop the generic paramater when defining the type S, I also get an error message that the generic type parameter is missing.
And just writing
type S = SensorSubject[Any]
doesn´t solve the problem either
EDIT2:
To clarify what my target is:
SensorSubject should be an Mixin Trait, such that i can use normal Sensors(not only MemorySensors) and that if I want I can add "with SensorSubject[Int]" to the creation of the Sensor and then it functions as a SensorSubject
Which means i can register an observer and the observer is notified when i change the value of the Sensors(that now functions as SensorSubject)
Here is an example how I would like to use the SensorSubject[T] Trait:
//creating a sensor WITH the SensorSubject Trait
val sensorWithSubject= new MemorySensor[Int] with SensorSubject[Int]
sensorWithSubject.registerObserver(..)
//creating a normal Sensor WITHOUT SensorSubject
val normalMemSensor = new MemorySensor[Int]
You didn't provide any example of expected usage so my guess might be wrong. Still here is my attempt:
trait Sensor[T] {
def value: T
def newValue(): Unit = {}
}
class MemorySensor[T] extends Sensor[T] {
var internalValue: T = null.asInstanceOf[T]
def setValue(newVal: T): Unit = {
internalValue = newVal
newValue()
}
def value: T = internalValue
}
//////////////////////////////////
trait AbstractSubject[S <: AbstractSubject[S, O], O <: AbstractObserver[S, O]] {
this: S =>
def register(observer: O) = {
//we could use mutable collection here too
observers = observer :: observers
}
private var observers: List[O] = List[O]()
def notifyObservers() = {
observers.foreach(o => o.notifyObserver(this))
}
}
trait AbstractObserver[S <: AbstractSubject[S, O], O <: AbstractObserver[S, O]] {
def notifyObserver(subject: S)
}
//////////////////////////////////
trait SensorSubject[T, S <: SensorSubject[T, S, O], O <: SensorObserver[T, S, O]] extends Sensor[T] with AbstractSubject[S, O] {
this: S =>
}
trait SensorObserver[T, S <: SensorSubject[T, S, O], O <: SensorObserver[T, S, O]] extends AbstractObserver[S, O]
//////////////////////////////////
class MemorySensorSubject[T] extends MemorySensor[T] with AbstractSubject[MemorySensorSubject[T], MemorySensorObserver[T]] {
override def setValue(newVal: T): Unit = {
super.setValue(newVal)
notifyObservers()
}
}
trait MemorySensorObserver[T] extends AbstractObserver[MemorySensorSubject[T], MemorySensorObserver[T]]
and with that you can do
def test(): Unit = {
val sensor = new MemorySensorSubject[Int]
val observer = new MemorySensorObserver[Int] {
override def notifyObserver(subject: MemorySensorSubject[Int]): Unit = {
println(s"New value of $subject is ${subject.value}")
}
}
sensor.register(observer)
sensor.setValue(42)
}
and the output will be
New value of so.Main$MemorySensorSubject#363ee3a2 is 42
Probably the most important thing here is that MemorySensorSubject is an explicitly named type that thus can be used as S in F-bound generic constraint
//File Animal.scala
abstract class Animal {
val name: String
def getSomething(tClass: TypeClass): String = {
tClass.tName.split('.').lift(0)
}
def apply(tClass: TypeClass): SomeOtherClassType = {
// something...
}
// File: DogSpike, this is used for some specific cases (overwrites
base class val)
object DogSpike extends Animal {
override val name: String = "Spike"
}
this call then works (calls apply)
myTransformation(() => DogSpike(this))
Now I would like to create a more generic object that one can pass arguments but I am unable to.
It would work to create a derived Object from Animal that takes one arguments and being able to use the apply call
object TheDog(name: String) extends Animal {
override val name: String = "Spike"
//...
}
not sure how to implicitly call Animal.apply for TheDog object where I could pass a parameter (name)
myTransformation(() => TheDog(this))
// also this seems to be incorrect "*Wrong top statement declaration*"
object TheDog(val n: String) extends Animal {
override val name: String = n
//...
}
As of *Wrong top statement declaration* (I can understand only this part of your question) - you can't have constructor in object as object is a singleton, so you should use a case class (ADT):
final case class TheDog(name: String) extends Animal
scala>TheDog("Spike")
res2_2: TheDog = TheDog("Spike")
val and companion object with apply is added automatically for case classes, so you don't need to define your own own apply in Animal. case class TheDog(val name: String) is same as case class TheDog(name: String).
I's also use traits instead of abstract class:
trait Animal {
val name: String
def getSomething: String = {
"Dog: " + name
}
}
I don't understand your TypeClass type, but if you really want type classes:
trait Animal {
def name: String
}
final case class TheDog(name: String) extends Animal
final case class TheCat(name: String) extends Animal
implicit class RichDog(dog: TheDog){
def getSomething: String = {
"Dog" + dog.name
}
}
implicit class RichCat(cat: TheCat){
def getSomething: String = {
"Cat: " + cat.name
}
}
scala> TheDog("Spike").getSomething
res4_5: String = "DogSpike"
scala> TheCat("Tom").getSomething
res4_6: String = "Cat: Tom"
About calling apply "implicitly", I don't know why would anyone need this, but:
trait AnimalFactory[A <: Animal] {
def apply(name: String)(implicit constructor: String => A) = constructor(name)
}
object TheeeDog extends AnimalFactory[TheDog]
implicit def createDog(name: String) = TheDog(name)
TheeeDog("Spike")
Of course you have to provide createDog and make it visible for a client, but it doesn't really make sense if you can just use ADTs and define additional required applys in companion object:
case class TheMouse(name: String)
object TheMouse{
def apply(isJerry: Boolean): TheMouse = if (isJerry) TheMouse("Jerry") else TheMouse("NotJerry")
}
TheMouse(true)
If you want to add some parameter to constructor, just add it:
class AnimalFactory(clazz: SomeClass){
def doSomething = clazz.name
def apply(name: String)
}
val dogFactory = new AnimalFactory(dogClassDescriptor)
val catFactory = new AnimalFactory(catClassDescriptor)
dogFactory("Spike")
catFactory("Tom")
You can even create a factory for factory (I wouldn't recommend - this solution already looks overcomplicated):
object AnimalFactory{ //please don't use classes for that - avoiding `new` is not their purpose
def apply(clazz: SomeClass) = new AnimalFactory(clazz)
}
val dogFactory = AnimalFactory(dogClassDescriptor)
//or even `val spike = AnimalFactory(dogClassDescriptor)("Spike")`
But still what's the point if you could just provide underlying clazz either as a member or just in a wrapper:
final case class ClazzWrapper[T <: Animal](clazz: SomeClass, animal: T)
I have a library where an abstract class Base[T] is over a type T supplied by the user. There are many specific Base[T] sub-classes, some are over types T and S, like Specific[T, S], but this is irrelevant. The user might specify any T of course while creating and instance, but I want to treat it as T with a trait AdditionalAbilities or in other words I want to 'gift' the user's type with AdditionalAbilities. How can I do that in Scala? I hope the title is correct for this question.
Example (might not be syntactically correct)
class Specific[T **with trait Additional**]() extends Base[T](){
def doSomething() : T = {
val something = new T()
something.ability(2)
println(something.additional)
something
}
}
trait Additional{
var additional : Integer
def ability(i : Integer) : Unit = {
additional = i
}
}
Would work with any T.
When you define a parametric class you can require the parameter type to descend from a certain type:
trait AdditionalAbilities {
def doStuff(): Unit = println("Hey There")
}
object NoAbility extends AdditionalAbilities {
override def doStuff(): Unit = ()
}
abstract class Base[T] { ... }
class Specific[T <: AdditionalAbilities] extends Base[T] {
def f(t: T): Unit = t.doStuff()
}
Then when you try to instantiate a Specific type:
scala> new Specific[Int] {}
<console>:13: error: type arguments [Int] do not conform to class Specific's type parameter bounds [T <: AdditionalAbilities]
scala> val b = new Specific[NoAbility.type] {}
b: Specific[NoAbility.type] = $anon$1#517cd4b
scala> b.f(NoAbility)
//did nothing
Also, if you want to add a behaviour to an existing concrete class, you can do so at the time of instantiation:
trait CoolAbilities { def doStuff(): Unit = println("Hey there") }
class A { }
scala> val a = new A with CoolAbilities
a: A with CoolAbilities = $anon$1#6ad3381f
scala> a.doStuff()
Hey there
Perhaps implicit classes could help? Implicit classes allow you to add functionality to an existing type without needing to modify the existing type, or be the one instantiating it (so that you could mix in a trait).
The following compiles, and prints: 3
class Specific[T] {
implicit class TAdditional(t: T) {
var additional: Integer = 0
def ability(i: Integer) = {
additional = i
}
}
def doSomething(t: T) = {
doSomethingAdditional(t)
}
private def doSomethingAdditional(t: TAdditional) = {
t.ability(3)
println(t.additional)
}
}
val s = new Specific[Int]
s.doSomething(5)
Note: We need to do something to make sure we are accessing the same instance
of TAdditional, that's why I made the private doSomethingAdditional method that takes a TAdditional as an argument. If we call ability and additional in 'doSomething', separate instances of TAdditional would be created when we try to access #ability and #additional, and '0' would be printed.
I have the following trait:
trait Mappable {
def toMap = {
val mappableFields = this.getClass.getDeclaredFields.filter(...)
...
}
}
mappableFields lists this.declaredFields and then applies static filters to the list; as such it is invariant for each class that implements Mappable, and ideally I'd like to be able to put it in the subclasses' singleton objects or something along those lines. My current solution is
object Mappable {
import scala.collection.mutable.Map
private val fieldMap = Map[Class[_], Array[Field]]()
def getFieldMap(clazz: Class[_]) = {
fieldMap.get(clazz) match {
case Some(array) => array
case _ => {
val mapFields = clazz.getDeclaredFields.filter(...)
fieldMap.put(clazz, mapFields)
mapFields
}}}}
trait Mappable {
def toMap = {
val mappableFields = Mappable.getFieldMap(this.getClass)
...
}
}
but I'm wondering if there's a better solution e.g. one that doesn't require a call to Map#get. I can't turn the trait into a class.
You could do something like this:
trait Mappable {
def companion: MappableCompanion
def toMap = {
val mappableFields = companion.mappableFields
...
}
}
trait MappableCompanion {
def thisClass: Class[_]
val mappableFields = thisClass.getDeclaredFields.filter(...)
}
Subtypes of Mappable would then also define a companion object:
class Foo extends Mappable {
def companion = Foo
}
object Foo extends { val thisClass = classOf[Foo] } with MappableCompanion
If you don't like the early initializer you can make MappableCompanion a class.
class MyModel(var username:String, var password:String) extends FrameworkModel
object MyModelQuery extends FrameworkQuery {
type T = MyModel
}
trait FrameworkQuery {
type T
//do something with that type
}
So I get a class and an object where the latter is mixing in a trait which is defined as an abstract type. Is there a way I could programmatically set the type to the type of MyModel class, so the client would not need to? ie "object MyModelQuery extends FrameworkQuery" would take care of it
Could you achieve a similar effect by just nesting the query in the model?
trait FrameworkModel {
val model = this
trait FrameworkQuery {
type T = model.type
def getModel: T = model
}
}
class UserModel extends FrameworkModel {
// model stuff...
object UserQuery extends FrameworkQuery {
// query stuff...
}
}
trait T{
type X = this.type
def x: X = this
}
object A extends T{
def b = "Yep"
}
scala> A.x.b
res0: java.lang.String = Yep