myaa
05/28/2020, 11:17 PMChild1
can be "accessed" using a Child1Accessor<T>
object, which will return a value of type T
.
abstract class Accessor<T>
abstract class Parent<A : Accessor<*>> {
fun <T> access(accessor: Accessor<T>) : T = TODO("implement")
}
class Child1Accessor<T> : Accessor<T>()
class Child1 : Parent<Child1Accessor<*>>()
class Child2Accessor<T> : Accessor<T>()
class Child2 : Parent<Child2Accessor<*>>()
val myIntVal: Int = Child1().access(Child1Accessor<Int>())
val myStringVal: String = Child1().access(Child1Accessor<String>())
val error: Int = Child2().access(Child1Accessor<Int>()) // should be an error, wrong type of accessor
my issue is: how do i restrict the accessor
parameter of access
to be a subclass of A
, the accessor type specified for that class, without using wildcard types, so that i know the type parameter of the accessor?
i tried the following:
fun <T, V> access(accessor: V) : T
where V : A,
V : Accessor<T> = TODO("implement")
but this gives me a Type parameter cannot have any other bounds if it's bounded by another type parameter
errorhenrikhorbovyi
05/28/2020, 11:50 PMaccess
function has nothing to do with the type A
of its class. 🤔
fun <T> access(accessor: Accessor<T>): T
So... if you just create a file and paste it:
It will work the samehenrikhorbovyi
05/28/2020, 11:50 PMmyaa
05/28/2020, 11:51 PMaccessor
be constrained to be a subclass of A
, in addition to being an instance of Accessor<T>
myaa
05/28/2020, 11:53 PMabstract class Accessor<T, C : Parent<C>>
abstract class Parent<C : Parent<C>> {
fun <T> access(accessor: Accessor<T, C>): T = TODO("test")
}
class Child1Accessor<T> : Accessor<T, Child1>()
class Child1 : Parent<Child1>()
class Child2Accessor<T> : Accessor<T, Child2>()
class Child2 : Parent<Child2>()
though it's kind of weird to me needing to have the child classes specify themselves as type parameters: Child1 : Parent<Child1>
and i'll admit i still haven't quite gotten my head around the C : Parent<C>
syntax. but it seems to be working the way i want it to now, so i suppose that will have to do unless someone has any ideas for a cleaner designhenrikhorbovyi
05/28/2020, 11:54 PMhenrikhorbovyi
05/28/2020, 11:57 PMhenrikhorbovyi
05/28/2020, 11:58 PMhenrikhorbovyi
05/28/2020, 11:59 PMAccessor<T>.access()
you are binding the type of access
function with the type of AccessorSam Garfinkel
05/29/2020, 4:42 AMSam Garfinkel
05/29/2020, 4:44 AMParent<*>
you’re going to need to use reflection..Sam Garfinkel
05/29/2020, 4:45 AMAccessor<String>
, Accessor<Int>
etc.myaa
05/29/2020, 7:05 AMclass Child1(
val field1: String,
val field2: Int,
val field3: String
) : Parent()
class Child2(
val field4: Boolean,
val field5: Double
) : Parent()
and further, i have functions that can take a child instance, along with a parameter saying what field to read from (actually set in a gradle dsl)
so i might define a set of accessors like
sealed class Child1Accessor<T>(val fieldName) : Accessor<T>() {
object FIELD1 : Child1Accessor<String>("field1")
object FIELD2 : Child1Accessor<Int>("field2")
object FIELD3 : Child1Accessor<String>("field3")
}
and then i can call a getter function like
val myVal = getField(child, Child1Accessor.FIELD1)
of course, reflection has to be used to actually get the corresponding value, but the key is that (1) there is a fixed set of accessors (sealed class), so i know the corresponding fields exist; (2) the accessors are typed, so i can do statically typed access (as opposed to having getField
take a string and return Any
). so i can define a function like
fun getStringField(obj: Child1, accessor: Child1Accessor<String>): String = obj.access(accessor)
this way the function can let the user choose what field to read from as long as it is a string field, while e.g. FIELD2
would be an invalid argument
the point of this is simply to enable something like the following from gradle:
myTask {
filterItems {
matchString = "hello"
inField = Child1Accessor.FIELD3
}
}