class C(val string: String) {
init {
println(string)
}
}
abstract class A {
abstract var string: String
val c = C(string)
}
class B : A() {
override var string = "string"
}
fun main() {
B()
}
kotlin playground for the problem
This code crash in runtime due to string var not initialized, how to do it right?
It's not a good practice and dangerous to use an abstract or open variable in the initialization of your class. And if you write this code in Android Studio or IntelliJ IDEA you will get this warning: Accessing non-final property string in constructor.
So what's happening here ? Well the super class which is A is going to be initialized first before totally initializing B, so this line of code val c = C(string) is going to run before even giving a value to string and that's what causing the error and you will get a NullPointerException because string is null.
How to fix this ? You can use lazy to initialize c like that:
val c by lazy { C(string) }
Now c is not going to be initialized only if you call it, so now it's safe
because you can't call it only if B is fully initialized.
You are initialising A's properties using non-final properties - in this case, you are initialising c with the abstract property string.
abstract var string: String
val c = C(string)
This in general could be unsafe. Subclasses could override the non-final property in such a way that it is initialised at a later point, which means any initialisation that depends on the non-final property in the superclass will get an undefined value.
In this case, this is exactly what happens. B overrides string so that it is initialised after A's primary constructor is called. As a result, when A's primary constructor is run, and c is initialised, string has the value of null.
To fix this, you can either make c lazy:
val c by lazy { C(string) }
This will only initialise c when you first access it, with whatever the value of string is at that time.
Alternatively, make c computed:
val c get() = C(string)
This will make a new C every time you access c, with the current value of string.
Related
learning kotlin & have been reading about subtypes/supertypes and variance from https://typealias.com/guides/star-projections-and-how-they-work/ & generally, this website. I have a question that I don't think is covered (or maybe I'm just confused). Suppose you have a
open class A
open class B : A()
Pretty clearly A is a supertype of B. But what about the following?
open class Foo<T : A> {
fun doSomething(temp: T)
}
open class SubFoo : Foo<B>() {
}
Is SubFoo a subtype of Foo?
fun input(input: Foo<A>)
fun output(): SubFoo<B>
val inputParam = SubFoo()
input(inputParam) // works?
val ret: Foo<A> = output() // also works??
Intuitively I think the above works as desired, and the answer to the above question is yes. But I'm not completely sure, nor do I have a concrete explanation other than it resolves in my head. Honestly there's like 3 things going on here, the typing of A/B, the typing of Foo vs SubFoo, and upper bounding, and I think I'm getting lost in it all. Thanks in advance!!
Is SubFoo a subtype of Foo?
No because Foo is not a type. Foo<A> and Foo<B> are types. Syntactically, Foo on its own is malformed unless the type parameter (the thing that goes in the <>) can be inferred.
In this case, SubFoo is a subtype of Foo<B> because it inherits from Foo<B>. SubFoo does not become a subtype of Foo<A> as a result of this though, so these do not work:
open class SubFoo : Foo<B>() {
override doSomething(temp: B) {
// do something that is specific to B
}
}
fun input(input: Foo<A>) { }
fun output(): SubFoo = SubFoo()
val inputParam = SubFoo()
input(inputParam) // compiler error
val ret: Foo<A> = output() // compiler error
The idea that you also become the subtype of SomeGenericType<SuperType> by inheriting SomeGenericType<Subtype> is called covariance. You can make a type parameter of covariant by adding out to it. For example, List<T> is declared like this:
public interface List<out E>
So List<String> is a subtype of List<Any>.
However, this only works if it is safe to do so. In the case of Foo, it is not safe at all for its type parameter to be covariant. Consider what would happen if val ret: Foo<A> = output() were allowed. I could do:
open class C : A()
val ret: Foo<A> = output() // suppose this worked
ret.doSomething(C())
From the type checker's perspective, this looks all fine. ret is a Foo<A>, so its doSomething takes an A. C inherits from A, so it can be passed to an A parameter.
But what actually happens when this is run? A SubFoo is returned by output(), and SubFoo only accepts Bs in its doSomething method. Oopsies!
Like
class A {
public var tip : String = ""
}
class B {
val tip2 = A().tip
println(tip2)
}
class C {
tiper("abc")
tiper("def")
tiper("ghi")
fun tiper(txt) {
A().tip = txt
B.showTip()
}
}
To be brief, I have a class B, which outputs a 'tip'. There is class C, which creates the text for the 'tip'. And I need to send a value from class C to class B. I tried doing it through class A, sending the value there and then reading it in class B to display it.
But in this case it just displays the value "", i.e. by default from class A.
Why isn't the value passed by class C taken instead?
The above code is greatly simplified and is a hypothetical description of what I actually have in my code. This is just an abstract example.
I'm a terrible Kotlin programmer, I was just asked to do this one job, so I don't know much about it, hope for your help.
You're creating a new object from type A every time you call it's constructor A().
Thus, inside tiper, you're creating an object of type A and setting the tip value on that object instance.
Then however, you create an object of type B which creates a new object of type A internally. This has no link to the first object of type A you've created. Thus, it does not contain the value you wanted to set but rather the default you've set, which is the empty string "".
Keeping close to our example, you can instead adjust the value on the object of type A that is embedded in the object of type B.
class A {
var tip: String = ""
}
class B() {
val tipHolder = A()
fun showTip() {
println(tipHolder.tip)
}
}
fun tiper(txt: String) {
val tipPrinter = B()
tipPrinter.tipHolder.tip = txt
tipPrinter.showTip()
}
fun main() {
tiper("abc")
tiper("def")
tiper("ghi")
}
However, without more details on the actual problem, it's hard to help you with the underlying problem you're trying to solve, as written by #aSemy in the comment section.
I'm trying to access the delegate of the property (id) of a class (FooImpl). The problem is, this class implements an interface (Foo), and the property in question overrides a property of this interface. The delegate only exists in the class (not that it could exist in the interface).
The problem is that using the :: operator on a variable of type Foo always returns the property of Foo, not that of the actual instance. The problem in code:
import kotlin.reflect.KProperty
import kotlin.reflect.KProperty0
import kotlin.reflect.jvm.isAccessible
interface Foo {
val id: Int
}
class FooImpl(
id: Int,
) : Foo {
override val id: Int by lazy { id }
}
val <T> KProperty<T>.hasDelegate: Boolean
get() = apply { isAccessible = true }.let { (it as KProperty0<T>).getDelegate() != null }
fun main() {
val foo: Foo = FooImpl(1)
println("foo::id.hasDelegate = ${foo::id.hasDelegate}")
println("(foo as FooImpl)::id.hasDelegate = ${(foo as FooImpl)::id.hasDelegate}")
}
This prints:
foo::id.hasDelegate = false
(foo as FooImpl)::id.hasDelegate = true
But this requires compile-time knowledge of the correct implementation. What I'm looking for is accessing the correct propert without having to specify FooImpl there.
The information is present at runtime because the least (!) intrusive workaround I have found so far is adding fun idProp(): KProperty0<*> to Foo and override fun idProp() = ::id to FooImpl and accessing the property using that.
Is there any better way than that?
I came up with this, but I don't know if there's a better way. The problem to work around is that getDelegate() has to return an actual instance of the delegate, so you need an instance of the class to be able to retrieve a delegate instance. It would really be nice if there was a hasDelegate property built in. Your version of hasDelegate will crash from the cast on unbound KProperty1's, which is all we have to work with when the specific class is unknown.
So to retrieve the delegate instance, we need to do search the class instance's member properties by name, which gives us a KProperty with covariant class type of the super-class type. Since it's covariant, we can call a consuming function like getDelegate() without casting to the invariant type. I think this logically should be safe, since we are passing an instance that we know has the matching type for the ::class that we retrieved the property with.
#Suppress("UNCHECKED_CAST")
fun <T: Any> KProperty1<T, *>.isDelegated(instance: T): Boolean =
(instance::class.memberProperties.first { it.name == name } as KProperty1<T, *>).run {
isAccessible = true
getDelegate(instance) != null
}
fun main() {
val foo: Foo = Foo2()
println("foo::id.hasDelegate = ${Foo::id.isDelegated(foo)}")
}
The problem here is that the owner of the property is resolved on compile time, not on runtime. When you do foo::id then foo (so FooImpl) become its bound receiver, but owner is still resolved to Foo. To fix this we wound need to "cast" property to another owner. Unfortunately, I didn't find a straightforward way to do this.
One solution I found is to use foo::class instead of foo::id as it resolves KClass on runtime, not on compile time. Then I came up with almost exactly the same code as #Tenfour04.
But if you don't mind using Kotlin internals that are public and not protected with any annotation, you can use much cleaner solution:
val KProperty0<*>.hasDelegate: Boolean
get() = apply { isAccessible = true }.getDelegate() != null
fun KProperty0<*>.castToRuntimeType(): KProperty0<*> {
require(this is PropertyReference0)
return PropertyReference0Impl(boundReceiver, boundReceiver::class.java, name, signature, 0)
}
fun main() {
val foo: Foo = FooImpl(1)
println(foo::id.castToRuntimeType().hasDelegate) // true
}
We basically create a new instance of KProperty, copying all its data, but changing the owner to the same type as its bound receiver. As a result, we "cast" it to the runtime type. This is much simpler and it is also cleaner because we separated property casting and checking for a delegate.
Unfortunately, I think Kotlin reflection API is still missing a lot of features. There should be hasDelegate() function, so we don't have to provide receivers, which is not really needed to check if property is delegated. It should be possible to cast KProperty to another type. It should be possible to create bound properties with some API call. But first of all, it should be possible to do something like: Foo::id(foo), so create KProperty of the runtime type of foo. And so on.
I have recently reviewed some kotlin codes, All nullable field initialized as null.
What is the difference between val x : String? = null and val x : String?
Should we initialize the nullable fields as null?
Everything, even nullable variables and primitives, need to be initialized in Kotlin. You can, as tynn mentioned, mark them as abstract if you require overriding. If you have an interface, however, you don't have to initialize them. This won't compile:
class Whatever {
private var x: String?
}
but this will:
interface IWhatever {
protected var x: String?
}
This too:
abstract class Whatever {
protected abstract var x: String?
}
If it's declared in a method, you don't have to initialize it directly, as long as it's initialized before it's accessed. This is the exactly same as in Java, if you're familiar with it.
If you don't initialize it in the constructor, you need to use lateinit. Or, if you have a val, you can override get:
val something: String?
get() = "Some fallback. This doesn't need initialization because the getter is overridden, but if you use a different field here, you naturally need to initialize that"
As I opened with, even nullable variables need to be initialized. This is the way Kotlin is designed, and there's no way around that. So yes, you need to explicitly initialize the String as null, if you don't initialize it with something else right away.
A property must be initialized. Therefore you have to do the initialization var x : String? = null. Not assigning a value is only the declaration of the property and thus you'd have to make it abstract abstract val x : String?.
Alternatively you can use lateinit, also on non-nullable types. But this has the effect, that it's not null, but uninitialized lateinit var x : String.
val x : String? will create an uninitialized variable or property, depending on where it's defined. If it's in a class (rather than a function), it creates a property, and you cannot create an uninitalized property unless it's abstract. For example take this code:
class MyClass {
val x : String?
}
This won't compile. You'll get Property must be initialized or be abstract.
This code, however, will compile
class MyClass {
fun test() {
val x : String?
}
}
However it's a bit pointless as you will not be able to refer to that variable: as soon as you do you'll get Variable 'x' must be initialized.
So yes, generally when defining a nullable member you should initialize it (e.g. with a value of null), unless it's abstract, in which case the overriding class should initialize it.
for example , I want to change all setters this way:
this.a = StringUtils.trim(a);
If it's a java bean, I can do this by modifying the code generating template of the ide. But Intellij seems not support to atomically add getter/setter for kotlin data class.
Is there a way to do this?
There is not a way to do this as of Kotlin 1.1.
A Kotlin data class, for the most part, is a class "to do nothing but hold data".
I think the closest you can get is to validate your data upon class initialization and make your data class properties read-only values. e.g.:
data class Data(val a: String) {
init {
require(a == a.trim())
}
}
The following won't throw an exception:
val a = Data("ab")
val b = a.copy(a = "abc")
While the following will:
val c = a.copy(a = "abc ")
It looks like if you declare the property as private, you can create your own getter/setters for accessing it. This example works for me.
fun main(args: Array<String>) {
var t = test("foo")
t.setHello("bar")
println(t)
}
data class test(private var hello: String) {
fun setHello(blah: String) {
this.hello = blah
}
}
But you will still have an issue when the property is passed in to the constructor. You will probably need to rethink how you are doing this, either declaring the field private and trimming it in the getter, or not using a data class for this instance.