I am stuck with a "simple" problem. I am working with files metadata. I would like to provide both a read-only view (trait ReadOnly) with just getters as well as a read-write view (trait ReadWrite) with getters and setters. Each read-write view must extend a read-only view.
trait ReadOnly
trait ReadWrite
trait BasicRO extends ReadOnly {
def foo: String
def bar: Int
}
class BasicRW extends ReadWrite with BasicRO {
def foo: String = ???
def foo_=( str: String ): Unit = ???
def bar: Int = ???
def bar_=( i: Int ): Unit = ???
}
So far so good. But now, I would like to add a snapshot method to ReadWrite which will return the corresponding ReadOnly view (the same that was extended by the ReadWrite sub-class). In pseudo-scala, I would like to define this constraint as:
trait ReadWrite[T <: ReadOnly] extends T {
def snaphsot: T
}
But it does not compile because class type required but T found (compiler message). Is there a way to express this constraint in the Scala type-system ?
Just to be clear, you want snapshot to be in a separate trait, but it can only be used in a class which also extends ReadOnly?
You can use a self type for this
trait ReadOnly[T]
trait ReadWrite[T] { self: ReadOnly[T] =>
def snapshot: T
}
trait BasicRO[T] extends ReadOnly[T] {
def foo: String
def bar: Int
}
abstract class class1[T] extends ReadOnly[T] with ReadWrite[T] // legal
abstract class class2[T] extends ReadWrite[T] // Not legal
abstract class class3[T] extends BasicRO[T] with ReadWrite[T] // legal
This doesn't make ReadWrite extend ReadOnly, but it both requires that the base object also mix in a ReadOnly (with the same type parameter) and gives ReadWrite access to ReadOnly's methods, while allowing the two traits to be subclassed entirely independently of eachother.
Related
interface I
open class Base(protected val i: I)
class Impl: I
class Derived(args:Bundle): Base( Impl(args) ) { // here I create an instance of Impl
private val my: Impl // See my question
}
My Base class depends on an interface I. But the implementation Impl is created in Derived class. I want to use Impl object in Derived class too. I can see the Base implementation and cast super.i to Impl but I don't want to depends on Base's implementation details. Can I store Impl somewhere to restore in to Derived.my member? I have a restriction from the library: a class has to have a constructor with args:Bundle argument, no more or less
If you are opposed to simply casting the superclass's property i as Impl, you could do this using a secondary constructor and making your primary constructor private:
class Derived private constructor(private val my: Impl): Base(my) {
constructor(args: Bundle): this(Impl(args))
}
In Kotlin we can modify a class with the sealed modifier to denote that the class can only be extended within the same module; or from the docs:
Sealed classes and interfaces represent restricted class hierarchies that provide more control over inheritance. All subclasses of a sealed class are known at compile time. No other subclasses may appear after a module with the sealed class is compiled. For example, third-party clients can't extend your sealed class in their code. Thus, each instance of a sealed class has a type from a limited set that is known when this class is compiled.
What I want to know is, can the same or similar behaviour be applied to class members; for example, consider the following code. All of the classes and interfaces exist in the same module:
// I don't want to seal this because it should be implementable beyond this module.
interface Hashable {
val hash: Hash
}
// I don't want to seal this because it should be extensible beyond this module.
abstract class Base : Hashable {
final override val hash: Hash get() = hashOf(...)
}
open class Derived : Base {
// This doesn't work because it's final in the base class.
final override val hash: Hash get() = hashOf(...)
}
What I essentially want to express is, "I (the developer) determine how a Base should create it's hash, until I (the developer) state otherwise in a derived class. Nobody else outside of this module has the ability to alter how each extension or derivative of Base may create its hash."
Is this possible?
You can create additional property with internal visibility:
abstract class Base : Hashable {
final override val hash: Hash get() = _hash
internal open val _hash: Hash get() = hashOf(...)
}
open class Derived : Base() {
override val _hash: Hash get() = hashOf(...)
}
abstract class ServerMock(param: String) {
protected var someVar = params + "123"
fun justMyVar() = someVar
}
Usage example:
class BaseServer(param: String) : ServerMock(param) {
val y = someVar
}
Can this class be marked as open and not abstract?
What is the difference between open and abstract class?
abstract class cannot be instantiated and must be inherited, abstract classes are open for extending by default. open modifier on the class allows inheriting it. If the class has not open modifier it is considered final and cannot be inherited.
You can not instantiate an abstract class. You either need to subclass or create an anonymous class using object. In abstract classes you can just declare function without implementing them (forcing the subclass to imlement them) or provide a default implementation.
abstract class BaseClass {
fun foo() // subclasses must implement foo
fun bar(): String = "bar" // default implementation, subclasses can, but does not have to override bar
}
// error: can not create an instance of an abstract class
val baseClass = BaseClass()
class SubClass : BaseClass {
// must implement foo
override fun foo() {
// ...
}
// can, but does not need to override bar
}
// declaring an anonymous class (having no name) using object keyword
val baseClass: BaseClass = object : BaseClass {
// must implement foo
override fun foo() {
// ...
}
// it is optional implementing bar
override fun bar(): String {
return "somethingElse"
}
}
A class that is neither abstract nor open is considered to be final and can not be extended.
If you want to allow subclassing you should mark it open.
class AClass
// error: This type is final, so it can not be inherrited from.
class BClass : AClass
open class CClass
class DClass : CClass
So if you want to allow BaseServer to be subclassed you should mark it open. If you also want to declare functions, but force subclasses to implement them you can replace open with abstract.
Documentation
Kotlin Abstract Classes
Kotlin Inheritance (incl. open)
Imagine you have 2 classes
Class Person [parent class]
Class Coder [sub/child class]
When you want to inherit Coder from Person you have to make Person open, so it is available to inherit from.
Meanwhile you can make objects from Person itself.
When you don't need to make objects from parent class(in our case it's Person) or you don't see any meaning creating objects from it you can use abstract instead of open.
It works the same way as open does. But the main difference is that you cannot make objects from Person(parent class) anymore.
Is there any way to create an instance of Derived but not call the constructor of Base?
open class Base(p: Int)
class Derived(p: Int) : Base(p)
You actually can do it
import sun.misc.Unsafe
open class Base(p: Int){
init {
println("Base")
}
}
class Derived(p: Int) : Base(p){
init {
println("Derived")
}
}
fun main() {
val unsafe = Unsafe::class.java.getDeclaredField("theUnsafe").apply {
isAccessible = true
}.get(null) as Unsafe
val x = unsafe.allocateInstance(Derived::class.java)
println("X = $x")
}
But don't, this solution is a low-level mechanism that was designed to be used only by the core Java library and not by standard users. You will break the logic of OOP if you use it.
this is not possible. The constructor of the derived class has to call (any) constructor of the base class in order to initialise the content(fields) of the base class.
This is also the same case in Java. Just that the default constructor is called by default (if no parameters are provided in the constructor), but if you have to choose between constructors with parameters, you always have to call them explicitly, because you have to choose which values to pass into the constructor.
You must always call a constructor of a super-class to ensure that the foundation of the class is initialized. But you can work around your issue by providing a no-arg constructor in the base class. Something like this:
open class Base(p: Int?){
val p: Int? = p
constructor(): this(null)
}
class Derived(p: Int) : Base()
The way you handle which constructor of the base class is default and which parameters are nullable, etc. will depend highly on the specific case.
We are refactoring an inherited method to use a type class instead - we would like to concentrate all of the method implementations in one place, because having them scattered among the implementing classes is making maintenance difficult. However, we're running into some trouble as we're fairly new to type classes. At present method is defined as
trait MethodTrait {
def method: Map[String, Any] = // default implementation
}
abstract class SuperClass extends MethodTrait {
override def method = super.method ++ // SuperClass implementation
}
class Clazz extends SuperClass {
override def method = super.method ++ // Clazz implementation
}
and so on, where there are a total of 50+ concrete classes, the hierarchy is fairly shallow (abstract class SuperClass -> abstract class SubSuperClass -> abstract class SubSubSuperClass -> class ConcreteClass is as deep as it goes), and a concrete class never extends another concrete class. (In the actual implementation, method returns a Play Framework JsObject instead of a Map[String, Any].) We're trying to replace this with a type class:
trait MethodTrait[T] {
def method(target: T): Map[String, Any]
}
class MethodType {
type M[T] = MethodTrait[T]
}
implicit object Clazz1Method extends MethodTrait[Clazz1] {
def method(target: Clazz1): Map[String, Any] { ... }
}
implicit object Clazz2Method extends MethodTrait[Clazz2] {
def method(target: Clazz2): Map[String, Any] { ... }
}
// and so on
I'm running into two problems:
A. Mimicking the super.method ++ functionality from the previous implementation. At present I'm using
class Clazz1 extends SuperClass
class Clazz2 extends SubSuperClass
private def superClassMethod(s: SuperClass): Map[String, Any] = { ... }
private def subSuperClassMethod(s: SubSuperClass): Map[String, Any] = {
superClassMethod(s) ++ ...
}
implicit object Clazz1Method extends MethodTrait[Clazz1] {
def method(target: Clazz1): Map[String, Any] = {
superClassMethod(target) ++ ...
}
}
implicit object Clazz2Method extends MethodTrait[Clazz2] {
def method(target: Clazz2): Map[String, Any] = {
subSuperClassMethod(target) ++ ...
}
}
but this is ugly, and I won't get a warning or error if I accidentally call a method too far up the hierarchy e.g. if Clazz2 calls superClassMethod instead of subSuperClassMethod.
B. Calling method on a superclass, e.g.
val s: SuperClass = new Clazz1()
s.method
Ideally I'd like to be able to tell the compiler that every subclass of SuperClass has a corresponding implicit object for method in the type class and so s.method is type-safe (or I'll get a compile time error if I've neglected to implement a corresponding implicit object for a subclass of SuperClass), but instead all I've been able to come up with is
implicit object SuperClassMethod extends MethodTrait[SuperClass] {
def method(target: SuperClass): Map[String, Any] = {
target match {
case c: Clazz1 => c.method
case c: Clazz2 => c.method
...
}
}
}
which is ugly and won't give me a compile-time warning or error if I've omitted a class since I can't define SuperClass as a sealed trait.
We'd be open to alternatives to type classes that would allow us to concentrate the method code in one place. method is only being called from two places:
A. Other method implementations, for example Clazz1 has a val clazz2: Option[Clazz2], in which case the method implementation in Clazz1 would be something like
def method = super.method ++ /* Clazz1 method implementation */ ++
clazz2.map(_.method).getOrElse(Map())
B. The top level Play Framework controller (i.e. the abstract class from which all of the controllers inherit), where we've defined a three ActionBuilders that call method, e.g.
def MethodAction[T <: MethodTrait](block: Request[AnyContent] => T) = {
val f: Request[AnyContent] => SimpleResult =
(req: Request[AnyContent]) => Ok(block(req).method)
MethodActionBuilder.apply(f)
}
I think type classes are not compatible with your scenario. They are useful when the types are disjoint, but you actually require that the instances are reflecting a super-type/sub-type hierarchy and are not independent.
With this refactoring, you are just creating the danger of the wrong instance being picked:
trait Foo
case class Bar() extends Foo
trait HasBaz[A] { def baz: Set[Any] }
implicit object FooHasBaz extends HasBaz[Foo] { def baz = Set("foo") }
implicit object BarHasBaz extends HasBaz[Bar] { def baz = FooHasBaz.baz + "bar" }
def test[A <: Foo](x: A)(implicit hb: HasBaz[A]): Set[Any] = hb.baz
val bar: Foo = Bar()
test(bar) // boom!
So you ended up re-writing the polymorphic dispatch with your pattern matcher in SuperClassMethod. You basically go OO -> FP -> OO, while rendering the idea of type classes unusable (to be open), ending up rather in a sum type (all sub types known).
#0__ is on to something-- implicit resolution occurs at compilation, so the type class instance that gets used for a given input will not depend on the runtime type of that input.
To get the behavior you want, you'd need to write some implicit definition that will reflect on the actual type of the object on which you want to call method to pick the right typeclass instance.
I think this is more of a maintenance problem than what you've got right now.
Simply put: If you want to have your implementation in one place, you should use case classes for your hierarchy:
abstract class SuperClass;
case class Clazz(...) extends SuperClass;
def method(o : SuperClass) : Map[String, Any] = o match {
case Clazz( ... ) => defaultMethod ++ ...
case ...
}
(Note that method may of course be recursive)
Since you can have an open Sum Type in scala (compiler won't warn about missing patterns, though), that should tackle your problem without having to abuse typeclasses.