This is the code snippet:
abstract class SuperClass {
init {
toOverride()
}
abstract fun toOverride()
}
class ChildClass : SuperClass() {
private val innerClass = InnerClass()
override fun toOverride() {
innerClass.doSomething()
}
class InnerClass {
fun doSomething() = Unit
}
}
class MainActivity : AppCompatActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
val superClass = ChildClass()
superClass.toOverride()
}
}
Calling superClass.toOverride() will cause null pointer exception but from ChildClass point of view innerClass is an immutable field. The problem is that in the constructor of SuperClass toOverride() is being called.
We don't have control over how SuperClass is written in a lot of cases. In android framework there is a lot of such cases, for example http://androidxref.com/8.0.0_r4/xref/frameworks/base/core/java/android/view/View.java#20160 will be called in the constructor of View.java. How do we address those issues? Adding null checks will trigger IDE warning and looks like kotlin compiler will trim the null check sometimes if you use ?. operator since it think the ?. is not necessary.
To start with, I'd read What's wrong with overridable method calls in constructors?
With that out of the way, and if you really can't control what the parent class does and you really really need it to do what it's doing, you can probably do something like this (Only showing the ChildClass:
class ChildClass : SuperClass() {
private val innerClass: InnerClass? = InnerClass()
init {
// beware: this is ONLY done because toOverride ONLY calls
// innerClass.doSomething(), you have to make sure that anything else
// there is idempotent
toOverride()
}
override fun toOverride() {
innerClass?.doSomething()
}
}
My recommendation would be to think if there's an alternative implementation to your class hierarchy.
Related
I'm trying to write a class that only allows certain methods to be called in a lambda of one function.
Basically, I want to achieve similar behaviour to how you can only call suspend functions in a suspend context.
Right now the closest I can get is this.
class MyClass {
fun runCommands(block: CommandContext.() -> Unit) {
// do prep work
block.invoke(commandContext)
// do cleanup work
}
val commandContext = CommandContext()
inner class CommandContext {
fun commandFunc() {} // only callable from the lambda
}
}
The issues I'm having with this is I can't make CommandContext private so you could always make your own instance and run the command externally. It is also unnecessary for it to be instantiatable but I can't make an "inner object."
Any ideas on how to do this cleaner?
Outer scope should know that there is a commandFunc() method in CommandContext class (and that this class actually exists). That's why it can't be private. But you can encapsulate its implementation, effectively making it private, keeping public only its interface:
interface CommandContext {
fun commandFunc()
}
class MyClass {
fun runCommands(block: CommandContext.() -> Unit) {
// do prep work
block.invoke(CommandContextImpl)
// do cleanup work
}
private object CommandContextImpl : CommandContext {
override fun commandFunc() {} //actual implementation
}
}
//Usage:
fun main() {
MyClass().runCommands { commandFunc() }
}
I have a custom Exception class which looks like this:
class GenericException(message: String?, errorCode: Int) : RuntimeException(message), GraphQLError {
.....
}
As you all know, RuntimeException extends Throwable which has a method called getMessage()
Now the issue is, this interface GraphQLError (which is a library interface) also has a method called getMessage()
As a result, compiler is complaining with this:
OK so I implement the method:
override fun getMessage(): String {
TODO("Not yet implemented")
}
Now I get this:
What am I supposed to do here?
What I guessed in the comments was right, kotlin allows multiple inheritence. It was indeed because of the Throwable class.
You can use #JvmField annotation to instruct the compiler not to generate getters and setters for the field and then create the getter/setter yourself.
interface HasMessage {
fun getMessage(): String
}
class GenericException(
#JvmField override val message: String?, // var is also possible
val errorCode: Int // I made it a property, might not be as well
) : RuntimeException(message), HasMessage {
override fun getMessage(): String {
// return of the super's getter, probably no use because you have field as property in this class
val superGetMessage = super<RuntimeException>.message
TODO("Not yet implemented")
}
}
Play with the code yourself.
Is there any way to pass this when using interface delegation? This would enable nice composability - but I found no way to do this.
Means something like:
interface Foo {
}
class FooImpl(bar: Bar) : Foo {
}
class Bar: Foo by FooImpl(this) {
}
as long as FooImpl doesnt need a parameter like this it works - but it would be great to access the other class there - perhaps someone knows a way. Otherwise I would also be interested if this is worth a KEEP if not - or if it will be impossible for some reason.
Delegation doesn't support this. The delegate has to be instantiated before the class that is delegating to it, so the delegate cannot rely on it for construction. Another gotcha is that although you can override functions of the delegate, if the delegate internally calls those functions, it calls the original version, not the override. The delegate really lives in its own world.
But you could set it up for the host to pass itself to the delegate in its initialization block:
interface Foo<T> {
var host: T
fun doSomething()
}
class FooImpl : Foo<Bar> {
override lateinit var host: Bar
override fun doSomething() {
println(host.name)
}
}
class Bar(val name: String): Foo<Bar> by FooImpl() {
init {
host = this
}
}
fun main() {
val bar = Bar("Hello world")
bar.doSomething()
}
This would unfortunately expose the host to the possibility of getting disconnected from its own delegate by outside classes, though. Maybe you could make the property throw an exception if assigned more than once.
Here's a property delegate that could do that:
private class SingleAssignmentVar<T>: ReadWriteProperty<Any, T> {
private var value: T? = null
private var assigned: Boolean = false
#Suppress("UNCHECKED_CAST")
override fun getValue(thisRef: Any, property: KProperty<*>): T {
if (!assigned)
error("Property has not yet been set.")
return value as T
}
override fun setValue(thisRef: Any, property: KProperty<*>, value: T) {
if (assigned)
error("Property may only be set once.")
assigned = true
this.value = value
}
}
fun <T> Delegates.singleAssignment(): ReadWriteProperty<Any, T> = SingleAssignmentVar()
You may split your Bar class in two parts, say backend and frontend.
Frontend will be responsible for declaring interface with delegates, backend will host delegates and act as composition target.
For example:
interface Foo {
fun sayHello(): String
}
class FooImpl(val bar: BarBackend) : Foo {
override fun sayHello() = "Hello from ${bar.compositionTarget()}!"
}
class BarBackend() {
val fooImpl: FooImpl = FooImpl(this)
fun compositionTarget() = "backend"
}
class BarFrontend(backend: BarBackend) : Foo by backend.fooImpl
fun main() {
val bar = BarFrontend(BarBackend())
println(bar.sayHello())
}
interface CrudRepo {
fun save()
fun saveAll()
}
interface CustomRepo : CrudRepo {
fun validate()
override fun save() {
validate()
saveAll() // can call saveAll, I need to call save on CrudRepo here
}
}
CrudRepo has a function save which doesn't do validation. I decide to write my own interface which extends CrudRepo and overrides the save method. In my own definition of save I want to validate and then call the save method on the CrudRepo.
What should I try ?
If you try using super, you'll get an error that says "abstract member cannot be accessed directly". This applies to interfaces and classes; you can't call bodyless abstract methods on super. Which means this doesn't compile:
interface CrudRepo {
fun save()
fun saveAll()
}
interface CustomRepo : CrudRepo {
fun validate()
override fun save() {
validate()
super.saveAll()
}
}
But change the CrudRepo interface to this:
interface CrudRepo {
fun save()
fun saveAll(){
}
}
And it does.
If you want an abstract example, here you go:
abstract class CrudRepo {
abstract fun save()
abstract fun saveAll()
}
class CustomRepo : CrudRepo() {
override fun saveAll() { }
override fun save() {
super.saveAll()
}
}
You'll get the same error message. This is because you cannot call unimplemented versions of a method. Also, let's take a look at inheritance.
interface CustomRepo : CrudRepo
class MyClass : CustomRepo
means MyClass is a child of both CrudRepo and CustomRepo. Both MyClass() as CustomRepo and MyClass() as CrudRepo would compile.
Now, what does this mean for your problem?
There is no save method in CrudRepo from the CustomRepo's point of view. It isn't implemented, and as such, you can't call it directly. Calling super.saveAll() as I showed above means call the saveAll method on my parent. super is a very strict keyword. However, saveAll() isn't overridden. You can call save, but you cannot call it on the super interface, because the super interface doesn't have a save method with a body. The last three words of that sentence are incredibly important here; since the method defined in the CrudBody interface doesn't have a body, you can't call it.
And, in addition, since you've overridden the method, any calls to it would result in recursion. If you do this:
override fun save(){
save()
}
it will call itself over and over, until it crashes. See What is a StackOverflowError?.
Now, if you have an actual class:
class CustomRepoImpl : CustomRepo{
override fun saveAll() {
}
override fun save() {
super.save()
}
override fun validate() {
}
}
notice how it calls super; it will now call the super method in CustomRepo. It's not required to, however.
And when you override save in CustomRepo, remember one thing: you're overriding a method of CrudRepo. Any child classes are no longer required to override it at all. The example implementation I had would compile without it. The reason saveAll works (without the super keyword) is because it is abstract, and doesn't reference the method in which it is called from. See the StackOverflowError link. If you call an overridden method, the one in the child class is called, unless the method is invoked on super.
And you can't call super on bodyless methods in a superclass or interface, because the language has no idea what you're pointing to, because there's nothing there.
TL;DR: you can't call the super method of an abstract/bodyless method. saveAll refers to the first implementation of the method, or the superclass if defined in a child. Calling save would result in a StackOverflowError.
I think Zoes answer contains everything you need to know about inheriting or overriding the save-method. If your CustomRepo wouldn't be an interface in the first place, you could use delegation, e.g.:
class CustomRepo(private val repo : CrudRepo) : CrudRepo by repo {
fun validate() {}
override fun save() {
validate()
repo.save()
}
}
So if you have any CrudRepo you just wrap it into your CustomRepo. That's it. All methods will be delegated to your CrudRepo by default and if you do not want that you just override the functions that need to work differently.
If you really want to keep your custom interface then I would probably do something as follows:
interface ValidatedCrudRepo : CrudRepo {
fun validate()
/**
* Ensures that [validate] is called before the actual [save].
*/
override fun save()
}
class CustomRepo(private val repo : CrudRepo) : ValidatedCrudRepo, CrudRepo by repo {
override fun validate() {}
override fun save() {
validate()
repo.save()
}
}
So if anyone uses ValidatedCrudRepo it is ensured that the validate should be called before an actual save and actually also documented that way.
Imagine I had an interface like:
interface MyInterface {
fun doSomething()
}
And I was interop-ing between Kotlin and Java. I now want a constant static instance of this interface but I want that to be part of the interface. I could do this:
interface MyInterface {
fun doSomething()
companion object {
val CONSTANT = object: MyInterface {
override fun doSomething() { ... }
}
}
}
but that means I need to write MyInterface.Companion.getCONSTANT(). #JvmField doesn't work here.
I've also tried:
interface MyInterface {
fun doSomething()
object CONSTANT: MyInterface {
override fun doSomething() { ... }
}
}
}
Which works in other Kotlin files (I can write MyInterface.CONSTANT) but I'd have to write MyInterface.CONSTANT.INSTANCE in Java. This solution seems the closest to what I want.
Any solutions? I want to be able to write MyInterface.CONSTANT in both Kotlin and Java and have them refer to a single static final object that implements the interface.
I believe I could also convert my Interface to an abstract class but that's the last resort.
The issue of not being able to use #JvmStatic in interfaces is tracked in this ticket: https://youtrack.jetbrains.com/oauth?state=%2Fissue%2FKT-6301
It is fixed by now and one comment says
Fix would be avaliable in 1.2.30 under '-language-version 1.3' option