running into some generics issues consider the fol...
# announcements
m
running into some generics issues consider the following class hierarchy, consisting of a set of classes each with an associated "accessor". the idea is that e.g.
Child1
can be "accessed" using a
Child1Accessor<T>
object, which will return a value of type
T
.
Copy code
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:
Copy code
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
error
h
Your
access
function has nothing to do with the type
A
of its class. 🤔
Copy code
fun <T> access(accessor: Accessor<T>): T
So... if you just create a file and paste it: It will work the same
You just created a lot of indirections 😄
m
yes, that's the point, it doesn't work the way i want at the moment; i'm trying to figure how to make
accessor
be constrained to be a subclass of
A
, in addition to being an instance of
Accessor<T>
though i did manage to get around this by instead having the accessors specify what classes they can be used with:
Copy code
abstract 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 design
h
I guess it can work
And what if...
I know it's not the final implementation but it can gives you ideias
If you create an extension of
Accessor<T>.access()
you are binding the type of
access
function with the type of Accessor
s
This seems like overimplementation to me. What is your use case here? I think this is the least verbose you can get:
If you want a
Parent<*>
you’re going to need to use reflection..
What you can do, though is add overloaded methods to support particular
Accessor<String>
,
Accessor<Int>
etc.
m
well, it's a bit complicated, and it certainly does involve reflection in the background, but my use case is a set of objects that have some fields:
Copy code
class 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
Copy code
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
Copy code
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
Copy code
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:
Copy code
myTask {
    filterItems {
        matchString = "hello"
        inField = Child1Accessor.FIELD3
    }
}