How can I use a variable in my inner class? - kotlin

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"
}
}
}

Related

Kotlin inner class can't resolve extension function declared in outer class

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.
}
}
}

Access a Kotlin Outer class using the inner object from somewhere else

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.

Clean way to access outer class by the implementing delegate class

I was thinking about such case (accessing outer class which uses current class to implement some stuff):
interface Does {
fun doStuff()
}
class ReallyDoes: Does {
var whoShouldReallyDo: Does? = null
override fun doStuff() {
println("Doing stuff instead of $whoShouldReallyDo")
}
}
class MakesOtherDo private constructor(other: Does, hax: Int = 42): Does by other {
constructor(other: ReallyDoes): this(other.also { it.whoShouldReallyDo = this }, 42)
}
fun main(args: Array<String>) {
val worker = ReallyDoes()
val boss = MakesOtherDo(other = worker)
boss.doStuff()
}
Expected output:
Doing stuff instead of MakesOtherDo#28a418fc
But can't do that, because of error:
Error:(15, 79) Cannot access '' before superclass constructor
has been called
Which targets this statement: other.also { it.whoShouldReallyDo = this }
How can I (if at all) fix above implementation?
The reason for the error is other.also { ... = this } expression accesses this of type MakeOtherDo and is also used as argument to MakeOtherDo constructor. Hence, this will be accessed as part of MakeOtherDo (unary) constructor before this has been initialized as an instance of Does (super)class.
Since the assignment does not affect the initialization of the super class, you can executed it in the constructor of MakesOtherDo after the super class has been initialized.
class MakesOtherDo private constructor(other: Does, hax: Int = 42): Does by other {
constructor(other: ReallyDoes): this(other, 42) {
other.also { it.whoShouldReallyDo = this }
}
}
It took me a few minutes to decipher what you were doing above, and really the problem has nothing to do with delegates. You can simplify it down to this:
class Wrapper(var any: Any? = null)
class Test(val wrapper: Wrapper) {
constructor(): this(Wrapper(this)) // Cannot access "<this>" before superclass constructor has been called
}
The concept of "this" doesn't exist yet when we're still generating arguments for its constructor. You just need to move the assignment into the block of the constructor, which is code that's run after this becomes available:
class Test(val wrapper: Wrapper) {
constructor(): this(Wrapper()){
wrapper.any = this
}
}
Or in the case of your example:
constructor(other: ReallyDoes): this(other, 42){
other.whoShouldReallyDo = this
}

Kotlin, how to assign callback implementation to a variable

I'm trying to assign a callback implementation of an interface (defined inside a class A) to a variabile defined inside another class B. Let's say that class A has the interface OnSomethingHappens which defines a doSomething method.
Inside class B I've defined my callback variable like this:
private lateinit var callback:A.OnSomethingHappens
I need to create an instance of class A passing callback variabile to the constructor in this way:
myinstanceA = A(callback)
I'm trying to assign an instance of an anonymous class that implements A.OnSomethingHappens using this code:
callback = object : A.OnSomethingHappens {
override fun doSomething(..){
//here I put the implementation of this method
}
}
but the compiler says "expecting member declaration" for my callback variable and "name expected" for object.
What I'm doing wrong?
Instead, I'm able to define and at the same time assign the callback variable in this way:
private var callback = object : A.OnSomethingHappens {
override fun doSomething(..){
//here I put the implementation of this method
}
}
Why? Which are the differences and a possible solution?
I'm trying to assign an instance of an anonymous class that implements A.OnSomethingHappens using this code: ...
This should work, but only inside a method:
class B {
private lateinit var callback:A.OnSomethingHappens
fun someMethod() {
callback = object : A.OnSomethingHappens { ... }
}
...
}
Given the error message and that private var compiles (which doesn't inside a method), you are trying to set it directly in the body of the class instead:
class B {
private lateinit var callback:A.OnSomethingHappens
callback = object : A.OnSomethingHappens { ... }
...
}
This is illegal: the only code you can write there is member definitions and init blocks.
Also, if you can initialize callback directly where it's defined or inside init, there's no point to lateinit in the first place.
It's not obvious from the code snippets cut down to such small pieces, but your issue is that you're writing down the assignment inside the body of a class, but not inside a function.
Here's an example of a valid declaration and immediate assignment:
class A {
var x: X? = X()
}
Here's an example of an invalid assignment, which places an arbitrary expression in the body of a class:
class A {
lateinit var x: X
x = X() // expression placed inside the class body, invalid syntax
someFunction() // extra example, calling functions here is invalid in the same way
}
Instead, you could put this initialization inside a function:
class A {
lateinit var x: X
fun initializeX() {
x = X()
}
}
Or inside an initializer block (in this case, you don't even need lateinit):
class A {
var x: X
init {
x = X()
}
}
While I couldn't explain how to solve your exact problem, because I can't quite understand what code is in which class, I hope these examples and explanation helped.
Hmm, let me propose a variant. It's more simple for me:
import android.util.Log
class SomeClass {
fun mainMethod() {
ClassWithCallback(
{ myBackValue: String ->
logMyString(myBackValue)
}
)
//simplify
ClassWithCallback({ logMyString(it) })
}
private fun logMyString(myBackValue: String) {
Log.d("SomeClass", myBackValue)
}
}
class ClassWithCallback(private val myCallBack: (myBackValue: String) -> Unit) {
init {
// we do something here and back it by callback
val myString = "Hello! Pass me back!"
myCallBack.invoke(myString.toUpperCase())
}
}
Using Kotlin lambdas. Hope this will help you.

Passing parameters to a custom getter in kotlin

I have been reading about properties in Kotlin, including custom getters and setters.
However, I was wondering if it is possible to create a custom getter with extra parameters.
For example, consider the following method in Java:
public String getDisplayedValue(Context context) {
if (PrefUtils.useImperialUnits(context)) {
// return stuff
} else {
// return other stuff
}
}
Note that the static method in PrefUtils has to have Context as a parameter, so removing this is not an option.
I would like to write it like this in Kotlin:
val displayedValue: String
get(context: Context) {
return if (PrefUtils.useImperialUnits(context)) {
// stuff
} else {
// other stuff
}
}
But my IDE highlights all of this in red.
I am aware I can create a function in my class to get the displayed value, but this would mean I would have to use .getDisplayedValue(Context) in Kotlin as well instead of being able to refer to the property by name as in .displayedValue.
Is there a way to create a custom getter like this?
EDIT: If not, would it be best to write a function for this, or to pass Context into the parameters of the class constructor?
As far as I know, property getter cannot have parameter. Write a function instead.
You can do this by having a property that returns an intermediate object that has a get and/or set operator with the parameters that you want, rather than returning the value directly.
Having that intermediate object be an inner class instance may be useful for providing easy access to the parent object. However, in an interface you can't use inner classes so in that case you might need to provide an explicit constructor parameter referencing the parent object when constructing your intermediate object.
For instance:
class MyClass {
inner class Foo {
operator fun get(context: Context): String {
return if (PrefUtils.useImperialUnits(context)) {
// return stuff
} else {
// return other stuff
}
}
}
val displayedValue = Foo()
}
...
val context : Context = whatever
val mc : MyClass = whatever
val y: String = mc.displayedValue[context]
You can do for example:
val displayedValue: String by lazy {
val newString = context.getString(R.string.someString)
newString
}