I have the following setup:
class A {
fun runA() {
}
inner class B {
fun runB() { // Proxy method for runA()
runA() // its okay to call it here
}
}
}
fun test() {
val obj = A().B()
obj.runA() // how to call this one??
obj.runB() // i do not want to add a proxy method for everything in A
}
Is there any way to call runA() when I only have the B object?
Every instance of B is always coupled to an instance of A, so it should theoretically be possible (and as I am able to call runA() from within B proves that)
Best solution that I currently have is providing a accessor of A within B like so:
inner class B {
fun a(): A = this#A
fun runB() {
runA()
}
}
and then call it like obj.a().runA()
Would be nice if I could just directly call obj.runA(), I don't see a reason why it shouldn't be technically possible other than the compiler not allowing it.
Thanks for your input!
B is not A. The compiler gives B a private reference to his own A, because he needs it to execute runA. But it is private, doesn't mean that you can access from there. You can just write runA and it works inside runB because an inner class has all the members of the parent class in scope, kind of like when you use a closure. See the analogy with this example:
class A {
fun runA() {
}
fun B {
{
runA() // its okay to call it here
}
}
}
fun test() {
val obj = A().B()
obj()
}
Similarly to what you saw with the inner class, inside the closure object created when you call the member B() you can access to all variables in scope (including the instance of A that was used to invoke B()).
If you want an object B that has all the members of A you should probably have a look at inheritance instead of inner classes. Or expose an accessor to A instance like you did.
Related
abstract class A {
abstract inner class B : A() {
init {
// How can I access the outer class?
}
}
}
In this example, this refers to the class B, this#A refers to the parent class. How can I access the outer class?
Test case:
abstract class A {
abstract inner class B : A() {
init {
println("Should display 'Parent': $this") // replace 'this' by something else?
}
}
}
class Parent : A() {
override fun toString() = "Parent"
inner class Child() : B() {
override fun toString() = "Child"
}
}
fun main() {
Parent().Child()
}
In your example, you created two objects - an instance of Parent, and an instance of Child.
Parent().Child()
^ ^
| |
1 2
So there is really only two different things that you can access from the init block of B. You can either access the Parent object using this#A, which is the object associated with the Child object, or the Child object itself using this (or redundantly this#B).
"Accessing the outer class", which presumably you mean A, is not something you can do, because there is not even such an instance of A.
You can however, invoke A's implementations of methods/properties using super#B.xxx (note that it is not super#A. Read it as "super of B"). Perhaps this is what you intended on doing in the first place.
For example, changing A to the following:
abstract class A {
override fun toString() = "A"
abstract inner class B : A() {
init {
println("Should display 'Parent': ${super#B.toString()}")
}
}
}
would print "A".
Note that this actually calls toString on the Child object, not the Parent object. You cannot do the same to the Parent object, because it is a different object. This init is creating the Child object, right?
In summary, this accesses some object that is in scope. super accesses a specific implementation of a method/property up the inheritance hierarchy.
Why inner class in Kotlin can't not access extension function declared in outer class as below:
class A{
val a = "as".foo() // LINE 1
class B{
val b = "as".foo() // LINE 2
}
fun String.foo(){}
}
On LINE 1 extension function is resolved but on LINE 2 the function is not resolved. Wonder why there is such limitation?
This is not an inner class, because you didn't use the keyword inner on it. It is merely a nested class. If you're familiar with Java, it's like a static inner class. Since it is not inner, it does not have any implicit reference to the outer class, and cannot make bare calls to members of the outer class since there is no specific instance to use the members of. It can however call members of the outer class on an instance of the outer class, so you could for example do the following:
class A{
val a = "as".foo()
class B{
val b = A().run { "as".foo() }
}
fun String.foo(){}
}
Even though foo is an extension function, it's also a member of A because of where it's declared. Using a scope function that causes a class to be a receiver inside the scope is one way to call one of its member extension functions from another class.
EDIT: Here's an example of one reason you'd want to declare an extension inside a class.
class Sample(val id: Int) {
private val tag = "Sample#$id"
fun String.alsoLogged(): String{
Log.d(tag, this)
return this
}
}
You can use this extension to easily log Strings you're working with inside the class (or when it's the receiver of run or apply). It wouldn't make sense to declare outside the class because it uses the private tag property of that class.
It's because Kotlin compiles your code to
public final class A {
#NotNull
private final Unit a;
#NotNull
public final Unit getA() {
return this.a;
}
public final void foo(#NotNull String $this$foo) {
Intrinsics.checkNotNullParameter($this$foo, "$this$foo");
}
public A() {
this.foo("as");
this.a = Unit.INSTANCE;
}
public static final class B {
public B() {
// You can't access A's foo() method here.
}
}
}
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() }
}
When I have two classes (A and B) and A has a function called myFunA which then calls myFunB (inside of class B), is it possible for code in myFunB to obtain a reference to the class A that is used to call myFunB? I can always pass the reference as a parameter but I am wondering if Kotlin has a way of allowing a function to determine the instance of the parent caller.
class A {
fun myFunA() {
val b = B()
b.myFunB() {
}
}
}
class B {
fun myFunB() {
// Is it possible to obtain a reference to the instance of class A that is calling
// this function?
}
}
You can do it like this:
interface BCaller {
fun B.myFunB() = myFunB(this#BCaller)
}
class A : BCaller {
fun myFunA() {
val b = B()
b.myFunB()
}
}
class B {
fun myFunB(bCaller: BCaller) {
// you can use `bCaller` here
}
}
If you need a reflection-based approach, read this.
I have this code using Kotlin:
class A{
var myVariable:String?=null
constructor(myVariable:String){
this.myVariable = myVariable
}
init {
println("Success !")
}
inner class B{
init {
println(myVariable)
}
}
}
fun main(args:Array<String>){
var b = A("test").B // this does not work
}
And unfortunately it does not work I get:
Error:(20, 23) Kotlin: Nested class 'B' accessed via instance reference
Error:(20, 23) Kotlin: Classifier 'B' does not have a companion object, and thus must be initialized here
How can I solve my problem?
When you do:
A("test").B
You're saying "Fetch something from an instance of A" - in this case, it's a class. However, this isn't the syntax you're looking for in your case. You can get mostly anything, but getting a reference is a separate issue. If you want to get a function, the syntax is different from getting a field or calling a function. Although this isn't very important, bit might still be worth keeping that in mind.
Since B is an inner class, you're so far entirely correct that you need an instance of A first. But you also need an instance of B. When you initialize B, it's still "connected" to the parent class, which is why you can access outer variables without any problems. However, it's still an initializable class - and you need to initialize it.
So you need to initialize B as well. The Kotlin syntax for this is pretty nice (where as the syntax in Java is slightly horrible) - all you need to add is () at the end.
So you'll end up with this:
val b = A("test").B()
// ...
This is because it's an inner class. If you had a static inner class (in Kotlin, that's a class within a class without the inner keyword), the initialization would've been A.B() - A isn't initialized in those cases.
You can also split up the initialization:
val a = A("test")
val b = a.B();
Once you have the variable, it's exactly like every other variable - the only difference here is the initialization.
Try this:
class A{
var myVariable:String?=null
constructor(myVariable:String){
this.myVariable = myVariable
}
init {
println("Success !")
}
inner class B{
init {
println(myVariable)
}
}
}
fun main(args:Array<String>){
var b = A("test").B() // You have to call the constructor here
}
You should create an instance (call constructor) of class B before accessing its members:
fun main(args:Array<String>) {
val b = A("test").B()
b.someFunction()
}
class A {
var myVariable:String? = null
constructor(myVariable: String) {
this.myVariable = myVariable
}
init {
println("Success !")
}
inner class B {
init {
println(myVariable)
}
fun someFunction() {
myVariable = "set new value to the variable"
}
}
}