https://kotlinlang.org logo
#getting-started
Title
# getting-started
d

DMITRY.

03/30/2021, 11:21 AM
Copy code
mongoOperations.findAndModify(searchQuery, update, T::class.java)
Cannot use 'T' as reified type parameter. Use a class instead. How make it work?
r

Roukanken

03/30/2021, 11:56 AM
where are you calling it from? you can't get class of type parameter, unless it's reified in inline function so if you are calling it from generic function, consider calling making it reified inline if you are calling it from a generic class or very complex function, you need to pass
KClass<T>
as parameter to it too, and use that instead of
T::class
d

DMITRY.

03/30/2021, 11:58 AM
I tried this:
Copy code
@PatchMapping(value = ["{taskId}"], consumes = ["application/json"])
    fun updateTask(@PathVariable("taskId") taskId: ObjectId, @RequestBody task: T, taskClass: KClass<T>): ResponseEntity<Void> {
        val document = Document()
        mongoOperations.converter.write(task, document)
        val update = Update()
        document.forEach { key: String, value: Any -> update[key] = value }
        mongoOperations.findAndModify(Query(Criteria("_id").`is`(taskId)), update, taskClass.java)
        return ResponseEntity<Void>(HttpStatus.NO_CONTENT)
    }
And then on the line with findAndModify I get this exception 😕
Copy code
java.lang.ClassCastException: class jdk.proxy3.$Proxy99 cannot be cast to class kotlin.jvm.internal.ClassBasedDeclarationContainer (jdk.proxy3.$Proxy99 is in module jdk.proxy3 of loader org.springframework.boot.devtools.restart.classloader.RestartClassLoader @2a01e51d; kotlin.jvm.internal.ClassBasedDeclarationContainer is in unnamed module of loader 'app')
r

Roukanken

03/30/2021, 12:02 PM
not sure what's the error, but Spring won't fill that
taskClass
argument properly, will it... won't it just pass null there?
d

DMITRY.

03/30/2021, 12:04 PM
yeah
Copy code
println(taskClass)
{}
r

Roukanken

03/30/2021, 12:05 PM
I assume the
T
is generics of the class containing this how exactly are you using this? Since no idea wth Spring does with generic controllers... is it just some base controller that you are extending from others?
eg I assume this is in
class GenericController<T>
and then you have classes like
class AbcController : GenericController<Abc>()
?
d

DMITRY.

03/30/2021, 12:17 PM
Yeah I have an abstract class
Copy code
abstract class Task(
    val id: ObjectId?,
    var deadline: java.time.LocalDateTime?,
)
Then classes like this
Copy code
class Change(
    id: ObjectId?,
    deadline: LocalDateTime?,
    worker: ObjectId?,
) : Task(id, deadline)
Interface:
Copy code
interface TaskController<T: Any> {
    val mongoOperations: MongoOperations

    @PatchMapping(value = ["{taskId}"], consumes = ["application/json"])
    fun updateTask(@PathVariable("taskId") taskId: ObjectId, @RequestBody task: T, taskClass: KClass<T>): ResponseEntity<Void> {
...
        mongoOperations.findAndModify(Query(Criteria("_id").`is`(taskId)), update, taskClass.java)
...
    }
}
And then controller:
Copy code
@RestController
@RequestMapping("change")
class ChangeController(
    override val taskRepository: ChangeRepository,
    override val mongoOperations: MongoOperations,
) : TaskController<Change>
It works If I specify directly inside interface
Copy code
mongoOperations.findAndModify(Query(Criteria("_id").`is`(taskId)), update, Change::class.java)
r

Roukanken

03/30/2021, 12:23 PM
well one solution I can think of it to change TaskController
Copy code
abstract class TaskController<T: Any> (
    private val taskClass: KClass<T>
) {
    // ...
}
then use it like this from the controllers that extend it:
Copy code
class ChangeController : TaskController<Change> (Change::class) {

}
tiny bit of repeating yourself, but it would definitely work
❤️ 1
or if you really want to leave it interface: (ofc, more repeating)
Copy code
interface TaskController<T: Any> {
    val taskClass: KClass<T>
}

class ChangeController : TaskController<Change> {
    override val taskClass = Change::class
}
tho I think it's definitelly an abstract class in spirit, even in your code now, not just an interface
❤️ 1
d

DMITRY.

03/30/2021, 12:30 PM
Oh wow, it works!
Thank you very much!
r

Roukanken

03/30/2021, 12:41 PM
basically the idea is, that the generic class has to request the T's class from w/e uses it (by constructor, or property, or function, or...) if user was using it, you could hide it in constructor, using reified inline factory function but here you can just specify it explicitly instead
24 Views