Given the following base class and one of its methods:
abstract class ServiceIf(val name: String) {
def run(args: Any*): Any = {}
An attempted subclass implementation is:
class RexecIf extends ServiceIf("Rexec") {
override def run(execParams: ExecParams, nLoops: Int) = {
However the "varargs" does not work:
Is this expected behavior - that is that varargs should/may not be used for base class methods?
Update I did not mention originally: a goal is to use the method within reflection for configuration file driven workflows. So using types-based approaches (as in the first answer) may be practical in some use cases but not in mine.
The overridden method has to be able to be called with any arguments the original accepted, not just the two you've provided. That is, a call recexif.run(1, 2, 3) has to be valid.
Also consider:
abstract class Base {
def run(arg: Any) = {}
}
class Derived extends Base {
override def run(arg: Int) = {}
}
This is similarly wrong, because the "override" does not implement the contract of the base method.
If you want the different implementations to accept different parameters for run, consider introducing an associated type or a type parameter. For example:
abstract class ServiceIf[RunArgs](val name: String) {
def run(arg: RunArgs): Any = {}
}
class RexecIf extends ServiceIf[(ExecParams, Int)]("Rexec") {
override def run(arg: (ExecParams, Int)) = {
// ...
}
}
You cannot override a general method with a more specific method. The override method must accept all the arguments that are accepted by the base method. So the code in the question can never be made to work.
You can, of course, have the override method fail unless it gets the values that it needs. Here is an example of how this works in this case:
abstract class ServiceIf(val name: String) {
def run(args: Any*): Any = {}
}
class RexecIf extends ServiceIf("Rexec") {
override def run(args: Any*) = {
val Seq(execParams: ExecParams, nLoops: Int) = args
}
}
This will fail with MatchError if the args do not match the pattern in the override method.
While this is not an "answer" it is maybe the closest workaround - to use a Seq explicitly instead of the varargs ?
abstract class ServiceIf(val name: String) {
def run(args: Seq[Any]): Any = {}
class RexecIf extends ServiceIf("Rexec") {
override def run(args: Seq[Any]) = {
val Seq(execParams: ExecParams, nLoops: Int) = args
I will go ahead with the above approach unless a more definitive answer appears - and would still receive its proper due.
Related
I have a trait with a method that's being used by 3 different classes, in one of the classes, I would like to override the method to accept an additional param, is this possible? how would one go about this in scala?
PS: I am new to Scala
Desired example:
trait Iterator {
def hasNext(a : String):
}
class Iterator extends Iterator {
override def hasNext(a : String, b: String) = {
...
}
}
I think there is something I don't quite well understand when I have the following code:
trait Configuration[F[_]] {
def get(key: String): F[ConfigValue]
}
class InMemoryConfig extends Configuration[Option] {
override def get(key: String): Option[ConfigValue] = ???
}
class InFileConfig(path: String) extends Configuration[Try] {
override def get(key: String): Try[ConfigValue] = ???
}
trait Logging {
def config: Configuration[_] // does not work
// ...
}
class DefaultLogging {
override val config = new InMemoryConfig
// ...
}
The name of the classes are pretty much meaningless, the general goal is to have a member in the trait without defining the param type in order to delay the choice until the implementation (actually in the DefaultLogging class).
Since Logging doesn't know which type constructor will be used for the Configuration it has to carry over it like any other type parameter.
Thus:
trait Logging[F[_]] {
def config: Configuration[F]
// ...
}
The same will apply to whatever depends on Logging it needs to specify the type or keep the dependency.
Thus, a common observation would be if such direct dependency is needed or not, but that becomes a matter of design and sometimes personal preference.
Given a superclass or trait, and assuming an open hierarchy, how can I enforce that all extending classes implement a particular type class?
For instance, assuming the type class Default
trait Default[T] { def default: T }
and some trait Super:
trait Super { }
I would like to enforce that the following (by itself) is not allowed:
class A(val i: Int) extends Super
...while the following is:
class B(val i: Int) extends Super
implicit val bHasDef = new Default[B] { def default = B(42) }
Assuming the above is possible, can I then access the type class evidence for the subtypes from a method within Super? I.e, something like:
trait Super {
def magic: Default[this.type] = ???
}
I hardly think you can enforce that, at least in a simple enough way, maybe it's possible with something more complex like shapeless.
What I would do is add some modifications to the super trait and make it take a self reference to Default
trait Default[T] { def default: T }
trait Super[T] {
self: Default[T] =>
}
class B(val i: Int) extends Super[Int] with Default[Int] {
override def default: Int = ???
}
class A(val i: Int) extends Super[Int] // doesn't compile, needs a Default
This should also solve the second part of your question, the disadvantage is that now one trait is bundled to the other.
Lets say I have a class like this
class Job(args:String) {
def config:Map[String,String] = ...
}
I want to create a trait that can be mixed in with this class to add more configuration options. So I did something like this
trait ExtraConfig { self:Job =>
override def config = self.config ++ Map("extra","config")
}
unfortunately this doesnt compile as self.config becomes self recursive.
I tried doing
trait ExtraConfig extends Job {}
but job is taking a constructor param and I dont know how to thread that through.
What would be the best way to solve this?
You are looking for abstract override modifier, but to use it you need explicitly extend the Job class, then in you trait you can use members of your superclass:
class Job(args:String) {
def config:Map[String,String] = Map.empty
}
trait ExtraConfig extends Job {
abstract override def config =
super.config ++ Map("extra" -> "config")
}
val job = new Job("name") with ExtraConfig
I'm not sure what the purpose of override keyword is, in scala. If I have
trait Shape { def foo(v: Int) }
class Triangle extends Shape { override def foo(v: Int) {} }
it behaves (apparently at least) exactly the same as it does without override.
In the case you are implementing an abstract method as in your example, it is not strictly necessary to add the override modifier.
However, in case you want to override a concrete method from the superclass, the override modifier is necessary. This is to avoid accidental overrides which could happen with mixing composition -- mixing-in traits during some refactoring could easily introduce a method definition that could be overridden by the method defined in the body of the class, hence the need for explicitly stating that a method is an override.
In your particular case, you got a comprehensive answer from axel22. I just want to add, that there is at least one more case where you may encounter override modifier. The keyword can also be used with trait methods.
Imagine that you have an abstract class:
abstract class Writer {
def print(str: String)
}
and its concrete implementation that prints on a console
class ConsoleWriter extends Writer {
def print(str: String) = println(str)
}
Now, you want to create a trait that will modify its behaviour. Look at the following implementation:
trait Uppercase extends Writer {
abstract override def print(str: String) =
super.print(str.toUpperCase())
}
Notice that a method has two modifiers: abstract and override. This is only allowed for traits and it means that the trait must be mixed into some class that has a concrete definition of the method in question
With the definition above, you can do:
val writer = new ConsoleWriter with Uppercase
writer.print("abc")
which will yield the result
ABC
Much in the same vain, you can add more traits:
trait WithSpaces extends Writer {
abstract override def print(str: String) =
super.print(str.split("").mkString(" ").tail)
}
Now when you call
val writer = new ConsoleWriter with Uppercase with WithSpaces
writer.print("abc")
you will see:
A B C
The above usage of an override modifier in traits is a distinguishing feature in scala and you won't see it in java.
It's for error checking.
Suppose you have
trait Shape { def foo(v: Int) = 1 }
class Triangle extends Shape { override def foo(v: Int) = 2 }
and then you change Shape to
trait Shape { def bar(v: Int) = 1 }
In that case the "override" will tell you that the foo in Triangle overrides nothing.
See also:
http://docs.oracle.com/javase/7/docs/api/java/lang/Override.html
http://en.wikipedia.org/wiki/C%2B%2B11#Explicit_overrides_and_final