Override a trait method for final class in Scala - scala

Consider the following:
trait TestTrait {
def doStuff()
}
final class TestClass {
// ...
}
I would like to instantiate an instance of TestClass that implements the method in the trait. The following does not compile:
// Illegal inheritance from final class TestClass
val t = new TestClass with TestTrait {
def doStuff() {
println("doing stuff")
}
}
This makes sense, since the anonymous class created would extend the final class. What I'm really after is an anonymous implementation of the trait mixed in to an instance of the final class.
The following works, but seems a bit roundabout. Is there a way to do this directly without the declared trait implementation?
trait TestTraitImpl extends TestTrait {
def doStuff() {
println("doing stuff")
}
}
val t = new TestClass with TestTraitImpl

As it turns out, you can't do the latter either.
final class TestClass
trait TestTrait
val t = new TestClass with TestTrait
Apparently, any Foo with Bar is creation of a new anonymous type, and thus final classes cannot have traits as mixins. This adds a great deal of theoretical significance to making a class final, as it prevents not just the concept of inheritance, but also stackable modification.

Method resolution rules are different in the two cases. In the first case an anonymous class is constructed first by whatever happens to be the methods/attributes of the type:
final class TestClass with TestTrait
and then you are trying to override a method of that, which conflicts with the final qualifier of TestClass.
In the second case you explicitly specify that you are overriding TestTrait behavior, and then the overriden behavior is mixed into TestClass.
I think it's perfectly fine to use the second method, and that it conveys more clearly what the intention is.

Related

Prohibit generating of apply for case class

I'm writing a type-safe code and want to replace apply() generated for case classes with my own implementation. Here it is:
import shapeless._
sealed trait Data
case object Remote extends Data
case object Local extends Data
case class SomeClass(){
type T <: Data
}
object SomeClass {
type Aux[TT] = SomeClass { type T = TT }
def apply[TT <: Data](implicit ev: TT =:!= Data): SomeClass.Aux[TT] = new SomeClass() {type T = TT}
}
val t: SomeClass = SomeClass() // <------------------ still compiles, bad
val tt: SomeClass.Aux[Remote.type] = SomeClass.apply[Remote.type] //compiles, good
val ttt: SomeClass.Aux[Data] = SomeClass.apply[Data] //does not compile, good
I want to prohibit val t: SomeClass = SomeClass() from compiling. Is it possible to do somehow except do not SomeClass to be case class?
There is a solution that is usually used if you want to provide some smart constructor and the default one would break your invariants. To make sure that only you can create the instance you should:
prevent using apply
prevent using new
prevent using .copy
prevent extending class where a child could call the constructor
This is achieved by this interesing patten:
sealed abstract case class MyCaseClass private (value: String)
object MyCaseClass {
def apply(value: String) = {
// checking invariants and stuff
new MyCaseClass(value) {}
}
}
Here:
abstract prevents generation of .copy and apply
sealed prevents extending this class (final wouldn't allow abstract)
private constructor prevents using new
While it doesn't look pretty it's pretty much bullet proof.
As #LuisMiguelMejíaSuárez pointed out this is not necessary in your exact case, but in general that could be used to deal with edge cases of case class with a smart constructor.
UPDATE:
In Scala 3 you only need to do
case class MyCaseClass private (value: String)
and it will prevent usage of: apply, new and copy from outside of this class and its companion.
This behavior was ported to Scala 2.13 with option -Xsource:3 enabled. You have to use at least 2.13.2 as in 2.13.1 this flag doesn't fix the issue.
So you can make the constructor private and ensure that T is also something different to Nothing.
I believe the best way to ensure the constructor is private (as well as many other things as #MateuszKubuszok show) is to use a (sealed) trait instead of a class:
(if you can not use a trait for whatever reasons, please refer to Mateusz's answer)
import shapeless._
sealed trait Data
final case object Remote extends Data
final case object Local extends Data
sealed trait SomeClass {
type T <: Data
}
object SomeClass {
type Aux[TT] = SomeClass { type T = TT }
def apply[TT <: Data](implicit ev1: TT =:!= Data, ev2: TT =:!= Nothing): Aux[TT] =
new SomeClass { override final type T = TT }
}
Which works like this:
SomeClass() // Does not compile.
SomeClass.apply[Remote.type] // Compiles.
SomeClass.apply[Data] // Does not compile.
You can see it running here.
If you want to prohibit using some of auto-generated methods of a case class you can define the methods (with proper signature) manually (then they will not be generated) and make them private (or private[this]).
Try
object SomeClass {
type Aux[TT] = SomeClass { type T = TT }
def apply[TT <: Data](implicit ev: TT =:!= Data): SomeClass.Aux[TT] = new SomeClass() {type T = TT}
private def apply(): SomeClass = ??? // added
}
val t: SomeClass = SomeClass() // doesn't compile
val tt: SomeClass.Aux[Remote.type] = SomeClass.apply[Remote.type] //compiles
val ttt: SomeClass.Aux[Data] = SomeClass.apply[Data] //doesn't compile
In principle, the methods (apply, unapply, copy, hashCode, toString) can be generated not by compiler itself but with macro annotations. Then you can choose any subset of them and modify their generation as you want.
Generate apply methods creating a class
how to efficiently/cleanly override a copy method
Also the methods can be generated using Shapeless case classes a la carte. Then you can switch on/off the methods as desired too.
https://github.com/milessabin/shapeless/blob/master/examples/src/main/scala/shapeless/examples/alacarte.scala
https://github.com/milessabin/shapeless/blob/master/core/src/test/scala/shapeless/alacarte.scala

How to "instantiate" an abstract class in the superclass?

I am implementing an extension of ml.Transformer in Spark; but this question is Scala specific. Here is an example object (part of a Class/Object pair):
abstract class UDFTransformer(func: UserDefinedFunction,
inputFieldNames: Seq[String],
outputFieldName: String) extends Transformer with MLWritable with Serializable {
... definitions here ...
}
object UDFTransformer extends MLReadable[UDFTransformer] {
// Since there are no parameters associted with the UDF, there is nothing to save!
class Writer(instance: UDFTransformer) extends MLWriter {
override protected def saveImpl(path: String): Unit = {}
}
abstract protected class Reader extends MLReader[UDFTransformer]
override def read: MLReader[UDFTransformer] = new Reader
override def load(path: String): UDFTransformer = super.load(path)
}
The new Reader does not compile because the class is abstract and cannot be instantiated. But; any child class will have to define it; along with its necessary members. I cannot just make read abstract as well, this gives me a warning Only classes can have declared but undefined methods.
The fundamental problem is that each child class of my UDFTransformer is going to wrap a specific UDF. Therefore, the reader needs to be able to generate a specific UDF object; this can't be declared in the superclass. But this 'factory' belongs in the companion object, not in the abstract class itself.
How can I go about building a companion object for an abstract class that can leave the definition of read undefined?
The normal way to do it is by creating an abstract class or trait for the companion objects. Something like
abstract class UDFTransformerCompanion[T <: UDFTransformer] extends MLReadable[T] {
abstract def read: MLReader[T]
override def load(path: String): T = super.load(path)
}
class SomeTransformer extends UDFTransformer { ... }
object SomeTransformer extends UDFTransformerCompanion[SomeTransformer] {
override def read: MLReader[SomeTransformer] = ...
}
Not sure why you have the load = super.load override, and it doesn't look like you can have a companion object for the UDFTransformer itself, at least not one extending this abstract class.
See GenericTraversableTemplate for a standard library example.

Trying to understand scala trait

I am new to scala. I don't understand scala traits properly. I have read it is similar to java interfaces but the methods need not be abstract. But how can I declare a scala trait and instantiate it in the following code. BTW, the following code is working fine.
trait fooable {
def foo: Unit = {
println("This is foo")
}
}
object Main {
def main(args: Array[String]): Unit = {
println("This is morking")
val foo = new fooable{}
foo.foo
}
}
Output -
This is morking
This is foo
Thanks in advance.
Scala traits are more general than both Java interfaces and abstract classes.
You can use a trait as an interface, you can use it to store some implementation, you can use it simply to define a common super-type:
trait Message
case class Text(text: String) extends Message
case class Data(data: ByteString) extends Message
Multiple traits can be 'mixed in' a class:
class MyClass extends TraitA with TraitB with TraitC
where any conflict with identically named methods is resolved by the simple rule: the last trait takes precedence. This code:
trait TraitA { def print() { println("A") } }
trait TraitB { def print() { println("B") } }
trait TraitC { def print() { println("C") } }
new MyClass.print()
will print "C".
Scala traits can't be instantiated. You are creating an anonymous class in you example. If you add an abstract method to your trait it will not compile.
Unrelated note:
It is a good practice to write braces "()" in methods with side effects. Your method foo has a side effect: it prints something. So you should write "foo()".
When you instantiate a trait you create an instance of an anonymous class that extends that trait it works the same way as creating anonymous classes of interfaces in java. If you had any unimplemented methods in the trait fooable the compiler would've forced you to implement them on spot when you created your anonymous class.
Indeed your code should work fine and output the same result you mentioned. However the important point to note here is TRAIT CAN NEVER EVER be instantiated. Its the same concept that Java interface can never ever be instantiated.
When scala compiler notice the code foo = new fooable{}, It internally creates an anonymous class which extends your fooable trait and thus it inherits foo() method due to inheritance. See following code snippet:
scala> val foo = new fooable{}
foo: fooable = $anon$1#277c0f21
Thus when you call foo.foo, At runtime it invokes a anonymous's class(i.e. $anon$1#277c0f21) inherited method foo().
The same understanding is true in case of Java as well, Following is perfectly legal java code:
Runnable r = new Runnable() {
public void run() {
System.out.println(" Cat");
}
};
r.run()
Happy learning !
An Scala Trait is abstract and can't be instantiated. In the code above its instantiated as
val foo = new fooable{}
and NOT as
val foo = new fooable()
The curly braces created some anonymous class which is not fooable but an empty one
What are Traits?
Traits are similar to interfaces in any other language. Scala allows traits to be instantiate during definition as well as during construction. For example if we have a trait and a abstract class
trait testTrait {
}
abstract class BaseClass {}
class Derive extends BaseClass with testTrait {
}
// ---------------------- OR----------------------
// Traits can be used during instantiation
class Derive extends BaseClass {
}
val classInst = new Derive with testTrait
Traits can also be chained using multiple with trait

Self-Type with private modifier

I would like to implement a trait with a self-type like the following:
trait HasEquipment {
this: {def loadEquipment: List[Equipment]} =>
//more methods
}
Now i can mix this trait into classes and these classes have to define a "loadEquipment" Method:
case class Course(id: Long) extends HasEquipment {
def loadEquipment: List[Equipment] = {
//implementation
}
}
However I don't want to expose to loadEquipment Method to everyone but only to the trait. So what i would like to do is make loadEquipment private in the Course class so that only the trait has access to it. Making it private prevents the trait from accessing the method.
Is there another way to make loadEquipment only accessible from within the trait?
Not sure why you want a self-type here. An abstract, protected member works just as well:
trait HasEquipment {
protected def loadEquipment: List[Equipment]
}
case class Course(id: Long) extends HasEquipment {
override protected def loadEquipment: List[Equipment] = ???
}
As a side note, structural types in Scala make use of reflection, which is the reason why they're usually avoided.

Why is "abstract override" required not "override" alone in subtrait?

I read the section of Programming in Scala where abstract override is introduced, but I'm still confused by what exactly is signified by the joining of these modifiers. The snippet of code in which these modifiers is used is pasted below:
trait Doubling extends IntQueue {
abstract override def put(x: Int) { super.put(2 * x) }
}
In particular, I am confused by the purpose of abstract in this case, and why we cannot achieve the expected results simply with the override keyword. If we did not include a call to super, would we need the keyword abstract? Why or why not? I'm looking for a detailed explanation of this keyword combo as it pertains to stackable traits.
The reason is that the base class method is abstract
abstract class IntQueue {
def get(): Int
def put(x: Int)
}
If you were to not put abstract on the trait you end up with the explanation you were seeking:
trait Doubling extends IntQueue {
override def put(x: Int) { super.put(2 * x) }
}
<console>:9: error: method put in class IntQueue is accessed from
super. It may not be abstract unless it is overridden by a member
declared `abstract' and `override'
override def put(x: Int) { super.put(2 * x) }
So - you would need to mark the method as abstract.
Here is the "other side" of the equation: if the methods do have implementations then it is not necessary to mark the trait's method as abstract:
abstract class IntQueue {
import collection.mutable._
val q = Queue[Int]()
def get(): Int = { q.dequeue() }
def put(x: Int) = { q.enqueue(x) }
}
It is now unnecessary to include abstract
trait Doubling extends IntQueue {
/* Look Ma! no abstract here ! */ override def put(x: Int) { super.put(2 * x) }
}
defined trait Doubling
The idea is that it's an incomplete override -- you still want to require the eventually concrete implementation of the trait to provide that method, even though you're modifying that hypothetical method's behavior. In other words, the method you're overriding isn't a full standalone implementation. It gives a similar effect as a method decorator might in Python.
As far as I can reason, a method on a trait is abstract override if and only if it calls super, but it breaks encapsulation to expect the client of the code to inspect the implementation of the method to know it needs a concrete implementation. Therefore, you must mark it abstract override to fully define the interface.
A part of late binding in scala traits posts; provides a very clear explanation; provided verbatim bellow (read the full post for more info):
The abstract base class provided an implementation of the requestApproval method. This is good since the leftmost trait calls this method. What happens if the base class’s method is abstract?
abstract class ApprovalRequest {
def requestApproval()
}
If we change this, we get a rather odd message from the compiler:
error: method requestApproval in class ApprovalRequest is accessed
from super. It may not be abstract unless it is overridden by a member
declared abstract and override
The combination of abstract and override tells the compiler that the final implementation of the method will be provided by the class mixing-in the trait. If we add the abstract keyword to the methods, we can no longer use our anonymous implementation of ApprovalRequest. That object can’t be created since the abstract override methods will be looking for an implementation of requestApproval and there isn’t one. Instead we have to create a new class that extends ApprovalRequest and implements requestApproval. We then mix the traits into an instance of that class.
class ApprovalDelegate extends ApprovalRequest {
override def requestApproval() {
println("and now we play the waiting game")
}
}
val adCampaign = new ApprovalDelegate with MarketingApprovalRequest
with FinanceApprovalRequest with ExecutiveApprovalRequest
Which will now give the output:
requesting approaval from executives
requesting approval from Finance
requesting approval from Marketing
and now we play the waiting game