Suppose I have a trait defined like
trait SomeTrait[A] {
def doSomething(): Seq[A]
}
and multiple classes that extend this trait as follows.
class SomeClass extends SomeTrait[SomeType] {
def doSomething(): Seq[SomeType] = {
:
}
}
Now in another class, I want to store the collection of instances of the classes extending the trait.
class AnotherClass {
pirvate val someClassInstances = mutable.Buffer[SomeTrait[_]]()
def addSomeClass[A](sc: SomeTrait[A]): Unit = {
this.someClassInstances += sc
}
}
How can I avoid using the existential type here?
Does the following meet your needs:
import scala.collection.mutable
trait SomeTrait[A] {
def doSomething(): Seq[A]
}
class SomeClass extends SomeTrait[Int] {
def doSomething(): Seq[Int] = ???
}
class AnotherClass {
private val someClassInstances = mutable.Buffer[SomeTrait[_]]()
def addSomeClass(sc: SomeTrait[_]): Unit = {
this.someClassInstances += sc
}
}
?
Let's say we have a base class(trait) with two methods(same name, different parameters, and not implemtented):
trait Base {
def compute(arg1:type1, arg2:type2): returnType
def compute(arg1:type1, arg2:type2, arg3:type3): returnType
}
And we have some classes inherit from Base. Suppose they are A, B, C. A and B implement "compute" with two args, while C implements "compute" with three args.
class A extends Base {
def compute(arg1:type1, arg2:type2): returnType = {
//detailed implementations
}
}
class B extends Base {
def compute(arg1:type1, arg2:type2): returnType = {
//detailed implementations
}
}
class C extends Base {
def compute(arg1:type1, arg2:type2, arg3:type3): returnType = {
//detailed implementations
}
}
Now, I have a set of these objects objs and I want to automatically choose which version of "compute" to use:
val name = objs.map{ x =>
val name = x.getClass.getSimpleName
name match {
case "C": x.compute(arg1, arg2, arg3)
case _: x.compute(arg1, arg2)
}
}
However, it compiles error:
class A(B the same) needs to be abstract, since method compute in trait Base of type (three parameters) is not defined
I'm confused at this error. Is that because all methods in Base must be implemented in its child classes(A,B,C)?
Is there any elegant fixes without editing class A and class B(because class C is most recently designed, and it's compute must add one more parameter, so I designed one more function above)?
Agree with #Mik378. But if you are in the process of migrating from the 2 args version to the 3 args version, you can:
trait Base {
// Mark this as deprecated
// No default implementation here because otherwise, A & B would need to
// be modified to add the 'override' keyword
#deprecated
def compute(arg1:type1, arg2:type2): returnType
// Provide a default implementation for old implementations e.g. A / B
def compute(arg1:type1, arg2:type2, arg3:type3): returnType =
compute(arg1, arg2)
}
// Convenience base class for new implementations e.g. C
abstract class NewBase extends Base {
override def compute(arg1: type1, arg2: type2): returnType =
throw new UnsupportedOperationException
}
class A extends Base {
def compute(arg1:type1, arg2:type2): returnType = {
//detailed implementations
}
}
class B extends Base {
def compute(arg1:type1, arg2:type2): returnType = {
//detailed implementations
}
}
// All new implementations extend 'NewBase' instead of 'Base'
class C extends NewBase {
override def compute(arg1:type1, arg2:type2, arg3:type3): returnType = {
//detailed implementations
}
}
And now, you can just use the 3-args version for old & new objs,
val name = objs.map(_.compute(arg1, arg2, arg3))
You'll either need to define compute(arg1:type1, arg2:type2, arg3:type3) in A and B and defined compute(arg1:type1, arg2:type2) in C or you can provide a default, no-op implementation in your trait
trait Base {
def compute(arg1:type1, arg2:type2) {}
def compute(arg1:type1, arg2:type2, arg3:type3) {}
}
I'd also recommend defining the return type explicitly in Base
Edit
A full (simplified) working example using case classes:
trait Base {
def compute(arg1: Int, arg2: Int): Int = 0
def compute(arg1: Int, arg2: Int, arg3: Int): Int = 0
}
case class A() extends Base {
override def compute(arg1: Int, arg2: Int): Int = arg1 + arg2
}
case class B() extends Base {
override def compute(arg1: Int, arg2: Int): Int = arg1 - arg2
}
case class C() extends Base {
override def compute(arg1: Int, arg2: Int, arg3: Int): Int = arg1 + arg2 - arg3
}
case class D(arg1: Int, arg2: Int, arg3: Int, objs: Seq[Base]) {
val computed = objs map (_ match {
case x: C => x.compute(arg1, arg2, arg3)
case x: Base => x.compute(arg1, arg2)
})
}
Have you heard about interface segregation principle?
The interface-segregation principle (ISP) states that no client should
be forced to depend on methods it does not use.1 ISP splits
interfaces that are very large into smaller and more specific ones so
that clients will only have to know about the methods that are of
interest to them. Such shrunken interfaces are also called role
interfaces.
Source: Wikipedia
Traits are in some ways similar to those named "interfaces".
Basically, you have to split Base trait.
Traits represent modules in Scala and it's a good practice to keep them small so that we increase their ability to be combined and get larger abstractions.
You would end up with two Traits:
(I merely altered the naming to be clearer)
trait Computation {
def compute(arg1:Int, arg2:Int): Unit
}
trait SpecificComputation {
def compute(arg1:Int, arg2:Int, arg3:Int)
}
class A extends Computation {
def compute(arg1:Int, arg2:Int) = {
//detailed implementations
}
}
class B extends Computation {
def compute(arg1:Int, arg2:Int) = {
//detailed implementations
}
}
class C extends SpecificComputation {
def compute(arg1:Int, arg2:Int, arg3:Int) = {
//detailed implementations
}
}
If you want a class D that should know about those two compute method variants, you write:
class D extends SpecificComputation with Computation {
def compute(arg1:Int, arg2:Int) = {
//detailed implementations
}
def compute(arg1:Int, arg2:Int, arg3:Int) = {
//detailed implementations
}
}
So I'm having some trouble with what I think is a pretty simple situation in trait implementation, and I'm hoping there is some simple solution that I'm missing. I'd like to have a method on a trait that accepts as a parameter (and returns as a value only the type of the concrete implementation that it is being called on. Specifically:
trait Foo {
type ConcreteFoo // what to put here?
def combine(that:ConcreteFoo):ConcreteFoo
}
class FooImpl1 extends Foo {
def combine(that:FooImpl1):FooImpl1 = {
// implementation
}
}
class FooImpl2 extends Foo {
def combine(that:FooImpl2):FooImpl2 = {
// implementation
}
}
Right now I have a type Self = FooImpl on the implementing classes, but I'd rather have something on the trait that takes care of it if possible.
This is exactly F-Bounded Polymorphism:
trait Foo[T <: Foo[T]]
def combine(that: T): T
}
class FooImpl1 extends Foo[FooImpl1] {
def combine(that: FooImpl1): FooImpl1 = {
???
}
}
class FooImpl2 extends Foo[FooImpl2] {
def combine(that: FooImpl2): FooImpl2 = {
???
}
}
You can add a type parameter to your trait like this:
trait Foo[A] {
def combine(that: A): A
}
class FooImpl1 extends Foo[FooImpl1] {
override def combine(that: FooImpl1): FooImpl1 = ???
}
class FooImpl2 extends Foo[FooImpl2] {
override def combine(that: FooImpl2): FooImpl2 = ???
}
When I extend traits I can choose which method implementation to use. Like here:
object Main {
def main(args: Array[String]): Unit = {
val c = new C
println(c.a)
println(c.b)
}
trait Parent {
def foo: String
}
trait A extends Parent {
override def foo = "from A"
}
trait B extends Parent {
override def foo = "from B"
}
class C extends A with B {
val b = super[A].foo
val a = super[B].foo
}
}
But if I want to do the same with self-types it's seems like it's not possible:
object Main {
def main(args: Array[String]): Unit = {
val c = new C with A with B
println(c.a)
println(c.b)
}
trait Parent {
def foo: String
}
trait A extends Parent {
override def foo = "from A"
}
trait B extends Parent {
override def foo = "from B"
}
class C {
self: A with B =>
val b = super[A].foo
val a = super[B].foo
}
}
This doesn't compile. Am I right and it's not possible? If I'm right, why is that and is there a workaround for it?
UPDATE:
Why do I needed in a first place? I was playing around with dependency injection using self-types instead of constructor injection. So I had a base trait Converter and child traits FooConverter and BarConverter. And I wanted to write it like that(which doesn't work of course):
object Main {
class Foo
class Bar
trait Converter[A] {
def convert(a: A): String
}
trait FooConverter extends Converter[Foo] {
override def convert(a: Foo): String = ???
}
trait BarConverter extends Converter[Bar] {
override def convert(a: Bar): String = ???
}
class Service {
this: Converter[Foo] with Converter[Bar] =>
def fooBar(f: Foo, b:Bar) = {
convert(f)
convert(b)
}
}
}
I thought it's because of generics, but it turned that it's not. So I was just wondering if it's possible to somehow invoke super method of chosen trait with self-types. Because with simple inheritance it's possible. As for my original problem I can write it like this and it will work:
object Main {
class Foo
class Bar
trait Converter[A] {
def convert(a: A): String
}
trait FooConverter extends Converter[Foo] {
override def convert(a: Foo): String = ???
}
trait BarConverter extends Converter[Bar] {
override def convert(a: Bar): String = ???
}
class Service {
this: FooConverter with BarConverter =>
def fooBar(f: Foo, b:Bar) = {
convert(f)
convert(b)
}
}
}
Probably tighter abstraction, but I'm not sure if it's bad for this kind of situation and if I need such broad abstraction like Converter[A] at all.
Calling super methods from already constructed type is impossible (you can do it only from the inside). In your example, you're trying to call foo on the instance self, which is constructed in runtime, so foo is virtual and could be overridden - compiler doesn't know which actual implementation is going to be called (formal vs real type problem). So technically - it's impossible to do what you want (call virtual method as a static one).
The naive hack is :
trait CC extends A with B {
val b = super[A].foo
val a = super[B].foo
}
class C {
self: CC =>
}
It basically provides encapsulation you want - you might wanna redefine a and b in class C as they're not going to be available (in type C itself) till you mix C with CC.
Note that in every example you provide (including my naive solution) - resulting val c has access to foo anyway and which exact foo is going to be called depends on how do you mix A and B (A with B or B with A). So, the only encapsulation you get is that type C itself isn't going to have foo method. This means that self-type gives you kind of a way to temporary close (make private) a method in "subclass" without violating LSP - but it's not the only way (see below).
Besides all of that, cake-injection that you're trying to implement is considered impractical by some authors. You might want to have a look at Thin Cake Pattern - as a remark, I successfully used something like this in real project (in combination with constructor injection).
I would implement your converter services this way:
class Foo
class Bar
trait Converter[A] {
def convert(a: A): String
}
object FooConverter1 extends Converter[Foo] {
override def convert(a: Foo): String = ???
}
object BarConverter1 extends Converter[Bar] {
override def convert(a: Bar): String = ???
}
trait FooBarConvertService {
def fooConverter: Converter[Foo]
def barConverter: Converter[Bar]
def fooBar(f: Foo, b: Bar) = {
fooConverter(f)
barConverter(b)
}
}
trait Converters {
def fooConverter: Converter[Foo] = FooConverter1
def barConverter: Converter[Bar] = BarConverter1
}
object App extends FooBarConvertService with Converters with ...
This allows you to change/mock converter implementation when putting it all together.
I'd also notice that Converter[Bar] is nothing else but Function1[Bar, String] or just Bar => String, so actually you don't need separate interface for that:
sealed trait FooBar //introduced it just to make types stronger, you can omit it if you prefer
class Foo extends FooBar
class Bar extends FooBar
trait FooBarConvertService {
type Converter[T <: FooBar] = T => String
def fooConverter: Converter[Foo]
def barConverter: Converter[Bar]
def fooBar(f: Foo, b: Bar) = {
fooConverter(f)
barConverter(b)
}
}
trait FooConverterProvider {
def fooConverter: Foo => String = ???
}
trait BarConverterProvider {
def barConverter: Bar => String = ???
}
object App
extends FooBarConvertService
with FooConverterProvider
with BarConverterProvider
You can also use def fooConverter(f: Foo): String = ??? instead def fooConverter: Foo => String = ???.
Talking about encapsulation - it's more weak here as you can access transitive dependencies, so if you really need it - use private[package] modifier.
Converters module:
package converters
trait FooBarConvertService {
type Converter[T <: FooBar] = T => String
private[converters] def fooConverter: Converter[Foo]
private[converters] def barConverter: Converter[Bar]
def fooBar(f: Foo, b: Bar) = {
fooConverter(f)
barConverter(b)
}
}
trait FooConverterProvider {
private[converters] def fooConverter: Foo => String = ???
}
trait BarConverterProvider {
private[converters] def barConverter: Bar => String = ???
}
Core module:
package client
import converters._
object App
extends FooBarConvertService
with FooConverterProvider
with BarConverterProvider
You can use objects object converters {...}; object client {...} instead of packages if you prefer.
This encapsulation is even stronger than self-type based one, as you can't access fooConverter/barConverter from the App object (in your example foo is still accessable from val c = new C with A with B):
client.App.fooBar(new Foo, new Bar) //OK
client.App.fooConverter
<console>:13: error: method fooConverter in trait FooConverterProvider cannot be accessed in object client.App
client.App.fooConverter
^
Keep in mind that self types are meant to allow you to require that any client code that uses the trait you are mixing in must also mix in another trait. In other words it is a way of declaring dependencies. But it is not classical inheritance. So when you say class C { self: A with B => } A and B actually are not there at the time. You have just defined that the client code has to mix in A and B in order to then mix in C.
But for your specific use case, it seems like you can accomplish the same goal with something like this code. In other words first create a third trait and then extend it into a specific class.
object DoubleSelfType extends App {
val c = new DoubleFoo
println(c.a)
println(c.b)
trait Parent {
def foo: String
}
trait A extends Parent {
override def foo = "from A"
}
trait B extends Parent {
override def foo = "from B"
}
trait C {
self: A with B =>
val a = ""
val b = ""
}
class DoubleFoo extends C with A with B {
override val b = super[A].foo
override val a = super[B].foo
}
}
I have a trait and a class that extends the trait. I can use the methods from the trait as follows:
trait A {
def a = ""
}
class B(s: String) extends A {
def b = a
}
However, when I use the trait's method in the constructor like this:
trait A {
def a = ""
}
class B(s: String) extends A {
def this() = this(a)
}
then the following error appears:
error: not found: value a
Is there some way to define default parameters for the construction of classes in the trait?
EDIT: To clarify the purpose: There is the akka-testkit:
class TestKit(_system: ActorSystem) extends { implicit val system = _system }
And each test looks like this:
class B(_system: ActorSystem) extends TestKit(_system) with A with ... {
def this() = this(actorSystem)
...
}
because I want to create common creation of the ActorSystem in A:
trait A {
val conf = ...
def actorSystem = ActorSystem("MySpec", conf)
...
}
It's a little bit tricky because of Scala initialization order. The simplest solution I found is to define a companion object for your class B with apply as factory method:
trait A {
def a = "aaaa"
}
class B(s: String) {
println(s)
}
object B extends A {
def apply() = new B(a)
def apply(s: String) = new B(s)
}