Is there a way to make an interface also include the methods defined by another interface in Go?
For example:
type BasicDatabase interface {
CreateTable(string) error
DeleteTable(string) error
}
type SpecificDatabase interface {
CreateUserRecord(User) error
}
I would like a way to specify that the SpecificDatabase interface contains the BasicDatabase interface. Similar to the way Go lets you do composition of structs.
This way my methods can take a type that implements SpecificDatabase, but still call CreateTable() on it.
This can be done the same way as when composing structs.
type BasicDatabase interface {
CreateTable(string) error
DeleteTable(string) error
}
type SpecificDatabase interface {
BasicDatabase
CreateUserRecord(User) error
}
Related
I'm curious about an example given in Kotlin documentation regarding sealed classes:
fun log(e: Error) = when(e) {
is FileReadError -> { println("Error while reading file ${e.file}") }
is DatabaseError -> { println("Error while reading from database ${e.source}") }
is RuntimeError -> { println("Runtime error") }
// the `else` clause is not required because all the cases are covered
}
Let's imagine the classes are defined as follows:
sealed class Error
class FileReadError(val file: String): Error()
class DatabaseError(val source: String): Error()
class RuntimeError : Error()
Is there any benefit for using when over using polymorphism:
sealed class Error {
abstract fun log()
}
class FileReadError(val file: String): Error() {
override fun log() { println("Error while reading file $file") }
}
class DatabaseError(val source: String): Error() {
override fun log() { println("Error while reading from database $source") }
}
class RuntimeError : Error() {
override fun log() { println("Runtime error") }
}
The only reason I can think of is that we may not have access to the source code of those classes, in order to add our log method to them. Otherwise, it seems that polymorphism is a better choice over instance checking (see [1] or [2] for instance.)
This is described as "Data/Object Anti-Symmetry" in the book Clean Code: A Handbook of Agile Software Craftsmanship by Robert C. Martin.
In the first example (Data style), you are keeping your error classes dumb with an external function that handles all types. This style is in opposition to using polymorphism (Object style) but there are some advantages.
Suppose you were to add a new external function, one that returns an icon to show the user when the error happens. The first advantage is you can easily add this icon function without changing any line in any of your error classes and add it in a single place. The second advantage is in the separation. Maybe your error classes exist in the domain module of your project and you'd prefer your icon function to be in the ui module of your project to separate concerns.
So when keeping the sealed classes dumb, it's easy to add new functions and easy to separate them, but it's hard to add new classes of errors because then you need to find and update every function. On the other hand when using polymorphism, it's hard to add new functions and you can't separate them from the class, but it's easy to add new classes.
The benefit of the first (type-checking) example is that the log messages do not have to be hardcoded into the Error subclasses. In this way, clients could potentially log different messages for the same subclass of Error in different parts of an application.
The second (polymorphic) approach assumes everyone wants the same message for each error and that the developer of each subclass knows what that error message should be for all future use cases.
There is an element of flexibility in the first example that does not exist in the second. The previous answer from #Trevor examines the theoretical underpinning of this flexibility.
With Kotlin 1.5 was introduce the sealed interface. Even that I know the difference between classes and interfaces, I'm not clear what are the best practices and beneficies of using sealed interface over sealed class
Should I always use interface now even when is a simple case? Or will be a case by case?
Thanks
Obs: Didn't found similar questions, only about sealed classes
A major reason to choose to use a sealed class instead of interface would be if there is common property/function that you don't want to be public. All members of an interface are always public.
The restriction that members must be public can be worked around on an interface using extension functions/properties, but only if it doesn't involve storing state non-publicly.
Otherwise, sealed interfaces are more flexible because they allow a subtype to be a subclass of something else, an enum class, or a subtype of multiple sealed interface/class hierarchies.
I would also add that sealed interface can be chosen instead of a class to mark an object with additional characteristics and use it in the when statement. For example let's say we have some number of classes that inherited from a sealed class Device:
sealed class DeviceItem(val name: String) {
object Camera : DeviceItem("Camera")
object Lamp : DeviceItem("Lamp")
// ... etc, more devices
}
And we need to use an instance of DeviceItem in when statement, but we don't want to handle all the items, only specific items:
fun onDeviceItemClicked(item: DeviceItem) {
when (item) {
// ....
}
}
In this case we should either add all device items to the when statement with an empty body for devices that we don't want to handle, and the code becomes cumbersome, or use else statement to handle those device items with the empty body. But if we use else we will not be notified of the error, when a new device item is added and requires some handling, which can lead to bugs. Starting from Kotlin 1.7 it will be a compilation error if when operator is not exhaustive. So basically to handle such cases we can provide a sealed interface and handle only items, which implement it:
sealed interface ClickableItem
sealed class DeviceItem(val name: String) {
object Camera : DeviceItem("Camera"), ClickableItem
object Lamp : DeviceItem("Lamp")
// ... etc, more devices
}
fun onDeviceItemClicked(item: ClickableItem) {
when (item) {
Camera -> { /* do something */ }
}
}
In this case when a new device item, which implements ClickableItem interface, is added there will be a compilation error, saying that when statement should be exhaustive and we must handle it.
I have a class that needs to pass a Protocol to an Obj-C function. I have a constructor that takes the Protocol, but as the class is a generic that also takes the same protocol, I thought I could optimise it. However, if I try to use the generic value when calling the function, it fails to compile. I've tried various combinations of ".self" and ".Type" and ".Protocol", both in the code and in the generic argument, and nothing works. Is there any way to achieve this?
This is a Playground project to show the problem.
import Foundation
#objc protocol TestProtocol {
}
class Test<P> {
init() {
test(p: P.self) // Fails to compile with: Cannot convert value of type 'P.Type' to expected argument type 'Protocol'
test(p: TestProtocol.self) // Compiles
}
func test(p: Protocol) {
}
}
let c = Test<TestProtocol>()
I'd like to implement some interface methods in another file, using extensions.
I have a feeling it's not possible, but I'd love to do that.
Is this possible?
Here is the idea :
MyClass.kt
class MyClass : MyInterface {
}
MyClassExtension.kt
override MyClass.MyInterface.method1() {
}
override MyClass.MyInterface.method2() {
}
That is not possible to implement the interface in the other file. There are still some possibilities.
You may split your implementation into several abstract classes, e.g. abstract class A : Interface, abstract class B : A and so on. Each class can be in its own file.
The second alternative, that does not let one implement an interface, rather split method implementations is called extension functions.
https://kotlinlang.org/docs/reference/extensions.html
Extension functions are only able to access public API of a class. Extension functions cannot implement interface methods in that case.
Use the following syntax for the declaration:
fun MyClass.method2() { ... }
I want to override toString() in an interface and have objects that implement that interface to default to using that method (eg: not shadowed)
interface SingletonObjectTrait {
fun toString(): String = this.javaClass.simpleName
}
Is there a straightforward way to define such an interface, preferably with minimal configuration at implementation
object MyEvent: SomeEventLogic(), SomeEventType, SingletonObjectTrait
class SomeEventLogic {}
interface SomeEventType {}
That's not possible, I'm afraid.
Method implementations in interfaces work much like default methods in Java: they're used only if the implementing class doesn't have an implementation already. But every class already inherits toString() from Any, so the default would never be used.
In fact, the compiler has a specific error for this — if you try to implement toString() in an interface, it says:
An interface may not implement a method of 'Any'
I can't see a good way around this.
As Erik says, one option is to change the interface to an abstract class; but of course that's not viable if any implementations already extend another class.
Another option might be to implement a different method in the interface, and in the comments instruct implementing classes to override toString() and call that method. Not automatic, but less work for implementers, and less repetition.
There isn't a great way to do this other than using maybe an annotation processor to add the missing override at compile time (by adding an annotation to the interface that you detect and you generate the overrides in the implementation class). This would work, but may be biting off more than you want to, and is likely out of scope of an answer here on Stack Overflow (seek "how to write an annotation processor for Java or Kotlin" and "KAPT" for the one engine that supports Kotlin processors).
Back to your code and why it will not work as-is:
An interface cannot have a method with a signature that matches any of the methods in Any class. And if you try to override one of them you will get a compilation error.
An interface may not implement a method of 'Any'
The minimal code to do something like you want is:
interface SingletonObjectTrait {
fun asString(): String = this.javaClass.simpleName
}
open class SomeEventLogic {}
interface SomeEventType {}
object MyEvent: SomeEventLogic(), SomeEventType, SingletonObjectTrait {
override fun toString(): String = asString() // hope they don't forget to call this!
}
There is no guarantee the implementer will call the trait asString() function but at least you can share the functionality with a one-liner in the implementing class.