I have next code:
class Generic<out T: Number>(value: T) {
var item: T? = value // T? marked as compile error: Type parameter
//T is declared as 'out' but occurs in 'invariant' position in type T?
private set
but this code
class Generic<out T: Number>(value: T) {
val item: T? = value
or this code
class Generic<out T: Number>(value: T) {
private var item: T? = value
is ok. Why?
in first block of code setter is private as in third block.
I try find answer in google, but find nothing
Related
I have a specific question about the usage of generics in Kotlin.
I want to create a function which takes a generic T as an argument.
It uses that to assign name from one of the classes: Class1 or Class2 to the local variable testString.
Unfortunately this is only possible when I check the type of the argument with the if conditions.
This leads to duplicate code. If I try to avoid that and use Line 12 I get this error during compile time: Unresolved reference: name
Is it possible in Kotlin to avoid the if conditions and use the testString assignment only once when the classes you are going to use have the same property with the same name?
Code:
fun main() {
val class1 = Class1("Foo1")
val class2 = Class2("Foo2")
}
class Class1(val name: String)
class Class2(val name: String)
fun <T> doStuff(classOneOrTwo: T) {
var testString: String
testString = classOneOrTwo.name //not working: Unresolved reference: name
if (classOneOrTwo is Class1) {
testString = classOneOrTwo.name
}
if (classOneOrTwo is Class2) {
testString = classOneOrTwo.name
}
}
You don't need generics here.
You can just write an interface that requires its implementers to have a name property.
interface HasName {
val name: String
}
Class1 and Class2 should implement the interface:
class Class1(override val name: String): HasName
class Class2(override val name: String): HasName
Then doStuff can be written as:
fun doStuff(classOneOrTwo: HasName) {
var testString = classOneOrTwo.name
// ...
}
You can make doStuff generic:
fun <T: HasName> doStuff(classOneOrTwo: T) {
var testString = classOneOrTwo.name
// ...
}
But you don't gain anything in particular by doing so.
Non-reified* generics are the most helpful when you want to establish some kind of "link", whether it be between parameters, or between parameters and the return type. For example, if your method is supposed to return the same type of thing as it takes:
fun <T> doStuff(foo: T): T { ... }
Or your method takes two parameters, and the second parameter must be the element type of the first parameter, which is a mutable list:
fun <T> doStuff(list: MutableList<T>, t: T) { ... }
* This paragraph doesn't quite apply to reified generics, which could be useful on their own.
Class1 and Class2 have nothing in common for the doStuff function to resolve the property name even though they were written exactly the same way, if you expect that just because you have a generic parameter T everything will be automatically be resolved, unfortunately the compiler doesn't know what T is here, aside from it being implcitly Any? type, (i.e <T: Any?>).
You're having a compile error here becase name is not a property of Any?
classOneOrTwo.name //not working: Unresolved reference: name
However, calling the doStuff function compiles fine because everything in Kotlin is a direct or indirect child of Any?
fun main() {
val class1 = Class1("Foo1")
val class2 = Class2("Foo2")
doStuff(class1)
doStuff(class2)
}
and if you try to invoke some function using classOneOrTwo param and pressed cltr+click on it, youll see its a function of the type Any?
fun <T> doStuff(classOneOrTwo: T) {
...
...
classOneOrTwo.toString() // <-- ctrl + click this you'll see its a function of Any?,
You should create a hierarchy (Inheritance) where Class1 and Class2 can inherit something from, in your case name
open class ParentClass(open val name: String)
class Class1(override val name: String) : ParentClass(name)
class Class2(override val name: String) : ParentClass(name)
fun <T: ParentClass> doStuff(classOneOrTwo: T) {
Log.e("DoStuff", classOneOrTwo.name) // now this works find because
}
Now it works because you explicitly tell the compiler that T is a type of ParentClass where the name property exists, and is inherited by your Class1 and Class2.
Back to your main function
doStuff(class1)
doStuff(class2)
prints,
Foo1
Foo2
I have something like this
fun validate(obj1: Any) {
// Here I am getting the KClass of the object
val objKClass = obj1::class
// Here I am casting the object using KClass
val obj1Cast = objKClass.safeCast(obj1)
// Then I want to iterate over its properties
for (prop in objKClass.memberProperties) {
//And get one BigDecimal? value
val bigDecimal: BigDecimal? = prop.get(obj1Cast) as BigDecimal?
}
}
This doesn't work, I get in relation to prop.get(obj1Cast):
Type mismatch.
Required:
Nothing
Found:
Any?
Is there another way to access the values of the memberProperties and cast them to BigDecimal? (for example) given an object with type Any as the input for my function?
Use generic type capture to solve this problem.
In your current code, prop is of type KProperty1<out Any, *>. This is because the type of obj1 is Any, so the type of objKClass is KClass<out Any>. There are no values that satisfy the type out Any when it's used as an input parameter, so you get a compile error.
The first step is to capture the type of obj1. We can do that by adding a generic type parameter for it, instead of using Any. Let's call the captured type T.
fun <T: Any> validate(obj1: T) {
val objKClass: KClass<out T> = obj1::class
val obj1Cast = objKClass.safeCast(obj1) ?: return
for (prop in objKClass.memberProperties) {
val bigDecimal: BigDecimal? = prop.get(obj1Cast) as BigDecimal?
}
}
Now the type of prop is KProperty1<out T, *>. This gets us one step closer, but we still have a compile error. That's because out T can only be used for an output value, but we want to pass a T into a method parameter.
Luckily, the safeCast can help us, because it will narrow the type of the value so it exactly matches the type of the class. We just need to give it some help by using a second method to capture the exact type of the class. Let's call the narrower exact type T2.
// Here we capture the type of the object as T,
// and get its class as KClass<out T>
fun <T: Any> validate(obj1: T) {
validate(obj1, obj1::class)
}
// Here we narrow the type of the object from T to T2
fun <T: Any, T2: T> validate(obj1: T, type: KClass<T2>) {
val obj1Cast = type.safeCast(obj1) ?: return
for (prop in type.memberProperties) {
val bigDecimal: BigDecimal? = prop.get(obj1Cast) as BigDecimal?
}
}
Now it works with no compilation errors. For more information on how this works, you can read about "generic type capturing".
I got an error. Like this :
Error 1 : Platform declaration clash: The following declarations have the same JVM signature (getData()Ljava/lang/Object;):
fun (): I defined in typeErasure2
fun getData(): I defined in typeErasure2
Error 2 : Platform declaration clash: The following declarations have the same JVM signature (getData()Ljava/lang/Object;):
fun (): I defined in typeErasure2
fun getData(): I defined in typeErasure2
fun main(args : Array<String>){
var te = typeErasure("Jennie")
println(te.getData())
var te2 = typeErasure2("Sam")
println(te2.getData())
}
class typeErasure<I>(name : I){
private val data : I = name
fun getData() : I = data
}
class typeErasure2<I>(name : I){
val data : I = name // error 1
fun getData() : I = data // error 2
}
when I use the private keyword the program can run, otherwise it will get an error. anyone can explain to me? :)
This has nothing to do with generics. The problem with your code is that
public fun getData(): I
Is an accesor for "data". But when "data" is a public field, then the accesor is redundant. So when you do:
val someValue = myObject.data
Then the compiler cannot tell if it should use the accessor getData() or it should just point to the field directly.
When getData is private, then the compiler clearly knows that it can't use it so then it will point to the field directly.
class typeErasure2<I>(name : I){
val data : I = name
fun getData() : I = data
}
fun main() {
val obj = typeErasure2<Int>(123)
println(obj.data) // <--- Ask yourself, what does this line do exactly?
}
class typeErasure2<I>(name : I){
val data : I = name
private fun getData() : I = data
}
fun main() {
val obj = typeErasure2<Int>(123)
println(obj.data) // <--- Here it is clear
// it is not the "getData" because that one is private,
// so it must be the public field "data" you are pointing to
}
Kotlin's logic of properties differ slightly from Java's fields.
Whenever you declare any variable in class, its getter and setters are automatically generated, and they can be customized with get() or set {} after it.
Declaring getVariable() manually will result in platform clash, as getter is already defined for the field in the variable declaration and you are creating function with the same name as well.
You can use #JvmField annotation to instruct the compiler to not generate any getter or setter for the field.
#JvmField
val data: I = name
fun getData(): I = data
The following code works fine and the call to the foo.get() extension function returns the correct type BarImpl.
open class Bar
class BarImpl: Bar()
class Foo<T : Bar>
inline fun <reified T : Bar> Foo<T>.get(): T {
return SomeMap(this).get(T::class)
}
class Activity {
lateinit var foo: Foo<BarImpl>
val barImpl = foo.get()
}
But when I try to move Foo<T>.get() into the class the type inference fails
class Foo<T : Bar> {
inline fun <reified T : Bar> get(): T {
return SomeMap(this).get(T::class)
}
}
class Activity {
lateinit var foo: Foo<BarImpl>
val barImpl = foo.get()
}
error: type inference failed: Not enough information to infer parameter T in inline fun get(): T
Please specify it explicitly.
val vm = foo.get()
^
How can I move the function into the class?
The extension function returns the result of the Foo type parameter. So the result type can be inferred from the receiver type.
And the member function result type has nothing in common with Foo type parameter except the name, which means nothing for a compiler. You can see that T in method and T in class are different types by writing and compiling the following code:
Foo<BarImpl>().get<BarImpl2>()
If you want to make get to be a member function which returns the result of Foo type parameter, you should remove type parameter from function and inject class instance via the constructor:
class Foo<T : Bar>(private val clazz: KClass<T>) {
fun get(): T {
return SomeMap(this).get(clazz)
}
companion object {
inline operator fun <reified T : Bar> invoke() = Foo(T::class)
}
}
I am trying to create delegate var properties with a delegate that does not provide a setValue(...) method. In other words, I need a property that I can reassign but that should get its value via the delegate as long as it hasn't been reassigned.
I am using the xenomachina CLI arguments parser library, which uses delegates. This works well as long as I have val properties. In some cases I need to be able to change those properties dynamically at runtime, though, requiring a mutable var. I can't simply use a var here, as the library does not provide a setValue(...) method in its delegate responsible for the argument parsing.
Ideally, I'd like something like this:
class Foo(parser: ArgParser) {
var myParameter by parser.flagging(
"--my-param",
help = "helptext"
)
}
which doesn't work due to the missing setter.
So far, I've tried extending the Delegate class with a setter extension function, but internally it also uses a val, so I can't change that. I've tried wrapping the delegate into another delegate but when I do that then the library doesn't recognize the options I've wrapped anymore. Although I may have missed something there.
I can't just re-assign the value to a new var as follows:
private val _myParameter by parser.flagging(...)
var myParameter = _myParameter
since that seems to confuse the parser and it stops evaluating the rest of the parameters as soon as the first delegate property is accessed. Besides, it is not particularly pretty.
How do you use delegates that don't include a setter in combination with a var property?
Here is how you can wrap a ReadOnlyProperty to make it work the way you want:
class MutableProperty<in R, T>(
// `(R, KProperty<*>) -> T` is accepted here instead of `ReadOnlyProperty<R, T>`,
// to enable wrapping of properties which are based on extension function and don't
// implement `ReadOnlyProperty<R, T>`
wrapped: (R, KProperty<*>) -> T
) : ReadWriteProperty<R, T> {
private var wrapped: ((R, KProperty<*>) -> T)? = wrapped // null when field is assigned
private var field: T? = null
#Suppress("UNCHECKED_CAST") // field is T if wrapped is null
override fun getValue(thisRef: R, property: KProperty<*>) =
if (wrapped == null) field as T
else wrapped!!(thisRef, property)
override fun setValue(thisRef: R, property: KProperty<*>, value: T) {
field = value
wrapped = null
}
}
fun <R, T> ReadOnlyProperty<R, T>.toMutableProperty() = MutableProperty(this::getValue)
fun <R, T> ((R, KProperty<*>) -> T).toMutableProperty() = MutableProperty(this)
Use case:
var lazyVar by lazy { 1 }::getValue.toMutableProperty()
And here is how you can wrap a property delegate provider:
class MutableProvider<in R, T>(
private val provider: (R, KProperty<*>) -> (R, KProperty<*>) -> T
) {
operator fun provideDelegate(thisRef: R, prop: KProperty<*>): MutableProperty<R, T> =
provider(thisRef, prop).toMutableProperty()
}
fun <T> ArgParser.Delegate<T>.toMutableProvider() = MutableProvider { thisRef: Any?, prop ->
provideDelegate(thisRef, prop)::getValue
}
Use case:
var flagging by parser.flagging(
"--my-param",
help = "helptext"
).toMutableProvider()
You could wrap your delegate with a class like this:
class DefaultDelegate<T>(private val default: Delegate<T>){
private var _value: T? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): T? =
_value?: default.value
operator fun setValue(thisRef: Nothing?, property: KProperty<*>, value: T?) {
_value = value
}
}
Usage:
class Foo(parser: ArgParser) {
var myParameter: Boolean? by DefaultDelegate(parser.flagging(
"--my-param",
help = "helptext"
))
}
If you need nullability:
class DefaultDelegate<T>(private val default: Delegate<T>){
private var modified = false
private var _value: T? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): T? =
if (modified) _value else default.value
operator fun setValue(thisRef: Nothing?, property: KProperty<*>, value: T?) {
_value = value
modified = true
}
}