Kotlin compile error: Only classes are allowed on the left side of a class literal

0

How are generics managed in Kotlin?:

I have an interface and its corresponding implementation generated with IntelliJ from java code. The interface that has generated me is:

package com.oesia.mako.commons.repository

import com.oesia.mako.commons.dto.Page

/**
 * Plantilla genérica para los DAO. Para no repetir los DAO's utilizamos
 * generics de java. Ya que todas los DAO's a priori hacen las mismas
 * operaciones.

 * @param T Clase de los objetos a persistir/manipular en BD.
 * *
 * @param PK Tipo de la primary key.
 * *
 * @param C Criteria para montar la cláusula where de las queries.
 * *
 * @param Q Tipo Query para un los objetos clase T.
 * *
 * *
 * @author José-Alberto Gilberte León ([email protected]).
 */
interface IRepository<T, PK, C, Q> {

    /**
     * Devuelve todos los objetos de tipo T que cumplan los criterios y el filtro si los tienen

     * @param namedQuery Nombre de la query customizada que se desea ejecutar.
     * *
     * @param criteria Dto con criterios de busqueda
     * *
     * @return List con todos los objetos de tipo T que cumplen los criterios
     */
    fun findByCriteria(namedQuery: String, criteria: C): List<T>

    /**
     * Devuelve todas los objetos de tipo T que cumplan los criterios y el filtro si los tienen
     * en un objeto Page.

     * @param namedQuery Query customizada que se desea ejecutar.
     * *
     * @param rowCountQuery Query de oonteo del número total de elementos
     * *
     * @param criteria Dto con criterios de búsqueda
     * *
     * @return Page con todas las entidades tipo T y contadores que cumplen el criterio.
     */
    fun findPageByCriteria(namedQuery: String, rowCountQuery: String, criteria: C): Page<T>

    /**
     * Devuelve todos los objetos de tipo T que cumplan los criterios y el filtro si los tienen

     * @param criteria Dto con criterios de busqueda
     * *
     * @return List con todos los objetos de tipo T que cumplen los criterios
     */
    fun findByCriteria(criteria: C): List<T>

    /**
     * Devuelve todas los objetos de tipo T que cumplan los criterios y el filtro si los tienen
     * en un objeto Page.

     * @param criteria Dto con criterios de búsqueda
     * *
     * @return Page con todas las entidades tipo T y contadores que cumplen el criterio.
     */
    fun findPageByCriteria(criteria: C): Page<T>

    /**
     * Obtiene un objeto T de la base de datos

     * @param id El identificador de la entidad T
     * *
     * @return La entidad T
     */
    fun findById(id: PK): T

    /**
     * Inserta un objeto T

     * @param  dto Un dto de la entidad T
     * *
     * @return La entidad T.
     */
    fun insert(dto: T): T

    /**
     * Actualiza un objeto T

     * @param dto Un dto de la entidad T
     * *
     * @return La entidad T.
     */
    fun update(dto: T): T

    /**
     * Elimina un objeto T

     * @param id del dto de tipo T
     * *
     * @return Número de filas borradas
     */
    fun delete(id: PK): Int

    /**
     * Elimina n objetos T por Criteria.

     * @param Criteria para borrar
     * *
     * @return Número de filas borradas
     */
    fun deleteByCriteria(criteria: C): Int
}

And the implementation:

package com.oesia.mako.commons.repository.impl

import java.io.Serializable
import java.lang.reflect.ParameterizedType
import java.util.*

import org.slf4j.*
import org.springframework.dao.EmptyResultDataAccessException
import org.springframework.jdbc.core.RowMapper
import org.springframework.jdbc.core.namedparam.MapSqlParameterSource
import org.springframework.jdbc.core.namedparam.NamedParameterJdbcOperations
import org.springframework.jdbc.core.support.JdbcDaoSupport

import com.oesia.mako.commons.repository.IRepository
import com.oesia.mako.commons.dto.Page
import com.oesia.mako.commons.dto.criteria.BaseCriteria
import com.oesia.mako.commons.dto.model.SqlParametrizable
import com.oesia.mako.commons.jdbc.Query

/**
 * Plantilla genérica para los DAO. Para no repetir los DAO's utilizamos
 * generics de java. Ya que todas los DAO's a priori hacen las mismas
 * operaciones.

 * @param T - Clase de los objetos a persistir/manipular en BD.
 * *
 * @param PK - Tipo básico de la surrogate key
 * *
 * @param R - RowMapper mapeador de filas sql a objeto de tipo T
 * *
 * @param C - Criteria para montar la cláusula where de las queries.
 * *
 * @param Q - Query para montar el sql de las queries.
 * *
 * *
 * @author José-Alberto Gilberte León ([email protected]).
 */
class Repository<T : SqlParametrizable<java.lang.String>, PK : Serializable, R : RowMapper<T>, C : BaseCriteria, Q : Query<C>> : JdbcDaoSupport(), IRepository<T, PK, C, Q> {
    private val log = LoggerFactory.getLogger(javaClass)
    private var rowMapperClass: Class<R>? = null
    private var queryClass: Class<Q>? = null
    var namedParameterJdbcOperations: NamedParameterJdbcOperations? = null
    private val DEFAULT_PAGE_SIZE = 10
    private val MINIMUN_PAGE_SIZE = 1
    private val MINIMUN_PAGE_NUMBER = 1
    private val SQL_WORD_LIMIT = " LIMIT "
    private val SQL_WORD_OFFSET = " OFFSET "

    private fun initializeRepository() {
        var genericClass: Class<*> = javaClass
        while (Repository<*, *, *, *, *>::class.java.simpleName != genericClass.superclass.simpleName) {
            if (genericClass.genericSuperclass is ParameterizedType)
                break
            genericClass = genericClass.superclass
        }
        if (genericClass.genericSuperclass is ParameterizedType) {
            val parameterizedType = genericClass.genericSuperclass as ParameterizedType
            rowMapperClass = parameterizedType.actualTypeArguments[2] as Class<R>
            queryClass = parameterizedType.actualTypeArguments[4] as Class<Q>
        }
    }

    /**
     * Constructor vacío
     */
    init {
        initializeRepository()
    }

    /**
     * Muestra en las trazas de log los parámetros de la query.

     * @param mapSqlParameterSource Parámetros de la consulta.
     */
    private fun debugSqlParameters(mapSqlParameterSource: MapSqlParameterSource) {
        if (isNotEmptyMapSqlParameterSource(mapSqlParameterSource)) {
            var key: String? = null
            var value: Any? = null
            for ((key1, value1) in mapSqlParameterSource.values) {
                key = key1
                value = value1
                log.debug(key + ": " + value)
            }
        }
    }

    private fun isNotEmptyMapSqlParameterSource(mapSqlParameterSource: MapSqlParameterSource?): Boolean {
        if (mapSqlParameterSource != null
                && mapSqlParameterSource.values != null
                && mapSqlParameterSource.values.entries != null) {
            return true
        } else {
            return false
        }
    }

    /**
     * Devuelve todos los objetos de tipo T que cumplan los criterios y el filtro si los tienen.

     * @param criteria Dto con criterios de busqueda
     * *
     * @return List con todos los objetos de tipo T que cumplen los criterios
     * *
     * @throws Exception
     */
    @Throws(Exception::class)
    override fun findByCriteria(criteria: C): List<T> {
        return findByCriteria(queryClass!!.newInstance().toSQLFetchRows(criteria), criteria)
    }

    /**
     * Devuelve todos los objetos de tipo T que cumplan los criterios y el filtro si los tienen.

     * @param namedQuery Query customizada que se desea ejecutar.
     * *
     * @param criteria Dto con criterios de busqueda
     * *
     * @return List con todos los objetos de tipo T que cumplen los criterios
     * *
     * @throws Exception
     */
    @Throws(Exception::class)
    override fun findByCriteria(namedQuery: String, criteria: C): List<T> {
        return namedParameterJdbcOperations!!.query(namedQuery, criteria.mapSQLParameterSource, rowMapperClass!!.newInstance())
    }

    /**
     * Devuelve todas los objetos de tipo T que cumplan los criterios y el filtro si los tienen
     * en un objeto Page.

     * @param criteria - Dto con criterios de búsqueda
     * *
     * @return Page con todas las entidades tipo T y contadores que cumplen el criterio.
     * *
     * @throws Exception
     */
    @Throws(Exception::class)
    override fun findPageByCriteria(criteria: C): Page<T> {
        if (criteria.pageSize < MINIMUN_PAGE_SIZE)
            criteria.pageSize = DEFAULT_PAGE_SIZE
        if (criteria.actualPage < MINIMUN_PAGE_NUMBER)
            criteria.actualPage = MINIMUN_PAGE_NUMBER
        else if (criteria.totalPages > 0 && criteria.actualPage > criteria.totalPages)
            criteria.actualPage = criteria.totalPages
        val query = queryClass!!.newInstance()
        return findPageByCriteria(query.toSQLFetchRows(criteria), query.toSQLRowCount(criteria), criteria)
    }

    /**
     * Devuelve todas los objetos de tipo T que cumplan los criterios y el filtro si los tienen
     * en un objeto Page.

     * @param namedQuery Query customizada que se desea ejecutar.
     * *
     * @param rowCountQuery Query para contar el número total de filas.
     * *
     * @param criteria - Dto con criterios de búsqueda
     * *
     * @return Page con todas las entidades tipo T y contadores que cumplen el criterio.
     * *
     * @throws Exception
     */
    @Throws(Exception::class)
    override fun findPageByCriteria(namedQuery: String, rowCountQuery: String, criteria: C): Page<T> {
        val sqlFetchRows = addLimitAndOffset(namedQuery, criteria)
        val rowMapper = rowMapperClass!!.newInstance()
        val res = namedParameterJdbcOperations!!.query(sqlFetchRows, criteria.mapSQLParameterSource, rowMapper)
        val totalRows = namedParameterJdbcOperations!!.queryForObject(rowCountQuery, criteria.mapSQLParameterSource, Int::class.java)
        criteria.totalPages = totalRows
        val resultPage = Page<T>()
        resultPage.content = res
        return calculateCountersPage(resultPage, criteria, totalRows)
    }

    private fun addLimitAndOffset(query: String, criteria: C?): String {
        val queryLimited = StringBuilder(query)
        queryLimited.append(SQL_WORD_LIMIT)
        queryLimited.append(criteria!!.pageSize)
        queryLimited.append(SQL_WORD_OFFSET)
        var offset = 0
        if (criteria != null && criteria.actualPage > MINIMUN_PAGE_NUMBER)
            offset = (criteria.actualPage - 1) * criteria.pageSize
        return queryLimited.append(offset).toString()
    }

    private fun calculateCountersPage(page: Page<T>, criteria: C?, totalRows: Int): Page<T> {
        page.totalElements = totalRows.toLong()
        if (totalRows == 0)
            page.totalPages = 0
        else if (criteria != null && criteria.pageSize != 0)
            page.totalPages = Math.ceil((totalRows / criteria.pageSize).toDouble()).toInt()

        if (page.content != null && page.content.size > 0)
            page.size = page.content.size
        else
            page.size = 0
        page.number = criteria!!.actualPage
        return page
    }

    /**
     * Obtiene un objeto T de la base de datos

     * @param id - El identificador de la entidad T
     * *
     * @return La entidad T
     */
    override fun findById(id: PK): T {
        val sql = queryClass!!.newInstance().toSQLFindById()
        val keyNamePrimary = queryClass!!.newInstance().namePrimaryKey
        val parameters = HashMap<String, Any>()
        parameters.put(keyNamePrimary, id)
        val rowMapper = rowMapperClass!!.newInstance()
        var dto: T? = null
        try {
            dto = namedParameterJdbcOperations!!.queryForObject(sql, parameters, rowMapper)
        } catch (e: EmptyResultDataAccessException) {
        }

        return dto
    }

    /**
     * Inserta un objeto T

     * @param dto - Un dto de la entidad T
     * *
     * @return La entidad T.
     */
    override fun insert(dto: T): T {
        namedParameterJdbcOperations!!.update(queryClass!!.newInstance().toSQLInsert(), dto.toMapSqlParameterSource())
        return dto
    }

    /**
     * Actualiza un objeto T

     * @param dto - Un dto de la entidad T
     * *
     * @return La entidad T.
     */
    override fun update(dto: T): T {
        val parameters = dto.toMapSqlParameterSource()
        val rows = namedParameterJdbcOperations!!.update(queryClass!!.newInstance().toSQLUpdate(), parameters)
        if (rows != 0)
            dto.version = dto.version!! + 1
        return dto
    }

    /**
     * Elimina un objeto T

     * @param id - Identificador del dto de tipo T
     * *
     * @return Número de filas borradas
     */
    override fun delete(id: PK): Int {
        val parameters = HashMap<String, Any>()
        parameters.put("ID", id)
        return namedParameterJdbcOperations!!.update(queryClass!!.newInstance().toSQLDeleteById(), parameters)
    }

    /**
     * Elimina n objetos T por Criteria.

     * @param Criteria para borrar
     * *
     * @return Número de filas borradas
     */
    override fun deleteByCriteria(criteria: C): Int {
        return namedParameterJdbcOperations!!.update(queryClass!!.newInstance().toSQLDelete(criteria), criteria.mapSQLParameterSource.values)
    }
}

The problem is that what is generated by IntelliJ does not compile in the following method in Kotlin:

class Repository<T : SqlParametrizable<java.lang.String>, PK : Serializable, R : RowMapper<T>, C : BaseCriteria, Q : Query<C>> : JdbcDaoSupport(), IRepository<T, PK, C, Q> {


private fun initializeRepository() {
    var genericClass: Class<*> = javaClass
    while (Repository<*, *, *, *, *>::class.java.simpleName != genericClass.superclass.simpleName) {
        if (genericClass.genericSuperclass is ParameterizedType)
            break
        genericClass = genericClass.superclass
    }
    if (genericClass.genericSuperclass is ParameterizedType) {
        val parameterizedType = genericClass.genericSuperclass as ParameterizedType
        rowMapperClass = parameterizedType.actualTypeArguments[2] as Class<R>
        queryClass = parameterizedType.actualTypeArguments[4] as Class<Q>
    }
}

I get the following error on the line: while (Repository < *, *, *, *, * > :: class.java.simpleName! = genericClass.superclass.simpleName) { Only classes are allowed on the left hand side of a class literal

Its equivalent in Java that compiles is:

public class Repository<T extends SqlParametrizable<java.lang.String>, PK extends Serializable, R extends RowMapper<T>, C extends BaseCriteria, Q extends Query<C>> 
    extends JdbcDaoSupport 
    implements IRepository<T, PK, C, Q> {


private void initializeRepository() {
    Class<?> genericClass = getClass();
    while (!Repository.class.getSimpleName().equals(genericClass.getSuperclass().getSimpleName())) {
        if (genericClass.getGenericSuperclass() instanceof ParameterizedType)
            break;
        genericClass = genericClass.getSuperclass();
    }
    if (genericClass.getGenericSuperclass() instanceof ParameterizedType) {
        ParameterizedType parameterizedType = (ParameterizedType) genericClass.getGenericSuperclass();
        rowMapperClass = (Class<R>) parameterizedType.getActualTypeArguments()[2];
        queryClass = (Class<Q>) parameterizedType.getActualTypeArguments()[4];
    }
}
    
asked by José-Alberto Gilberte León 16.08.2017 в 12:27
source

1 answer

0

It seems to be fixed by putting the following:

private fun initializeRepository() {
    var genericClass: Class<*> = javaClass
    while (Repository::class.java.simpleName != genericClass.superclass.simpleName) {
        if (genericClass.genericSuperclass is ParameterizedType)
            break
        genericClass = genericClass.superclass
    }
    if (genericClass.genericSuperclass is ParameterizedType) {
        val parameterizedType = genericClass.genericSuperclass as ParameterizedType
        rowMapperClass = parameterizedType.actualTypeArguments[2] as Class<R>
        queryClass = parameterizedType.actualTypeArguments[4] as Class<Q>
    }
}
    
answered by 16.08.2017 в 12:31