1
votes

translate a springboot java demo into a kotlin one,and run into a type inference failed problem.

it is a repository fun to get back the target result

package tacocloud.data

import org.springframework.beans.factory.annotation.Autowired
import org.springframework.jdbc.core.JdbcTemplate
import org.springframework.stereotype.Repository
import tacocloud.Ingredient
import tacocloud.Type
import java.sql.ResultSet
import java.sql.SQLException


@Repository
class JdbcIngredientRepository
@Autowired
constructor( private val jdbc: JdbcTemplate) : IngredientRepository {
    override fun findAll(): Iterable<Ingredient> {
        return jdbc.query("select id, name, type from Ingredient"
        ) { rs, rowNum -> this.mapRowToIngredient(rs, rowNum) }
    }
    override fun findById(id: String): Ingredient {
        return jdbc.queryForObject(
                "select id, name, type from Ingredient where id=?",
                { rs, rowNum -> mapRowToIngredient(rs, rowNum)}, arrayOf(id))
    }
    @Throws(SQLException::class)
    private fun mapRowToIngredient(rs: ResultSet, rowNum: Int): Ingredient {
        return Ingredient(
                rs.getString("id"),
                rs.getString("name"),
                Type.valueOf(rs.getString("type")))
    }
    override fun save(ingredient: Ingredient): Ingredient {
        jdbc.update(
                "insert into Ingredient (id, name, type) values (?, ?, ?)",
                ingredient.id,
                ingredient.name,
                ingredient.type.toString())
        return ingredient
    }
}

the findById funtion keeps saying that "Error:(29, 21) Kotlin: Type inference failed. Expected type mismatch: inferred type is Ingredient? but Ingredient was expected".the delegate funtion mapRowToIngredient(rs: ResultSet, rowNum: Int): Ingredient has return a Ingredient,not a Ingredient?

any ideas?

  1. List item
1

1 Answers

1
votes

JdbcTemplate, I suppose, is being compiled from Java source code file, and in Java any reference can point to null. That's why queryForObject returns a nullable type - Kotlin tends to treat all Java's references return declarations to be nullable (refer to "platform types" for more info).

And if queryForObject returns null, then the mapper function you provide will be just omitted and null will be returned from the function eventually.

It is possible to either make findById function return a nullable type (change the declaration so that it returns an Ingredient?), specify a default object to return if queryForObject returned null (e.g. jdbc.queryForObject(...) ?: DEFAULT_RESPONSE) or make use of force "unboxing" to the non-null type (e.g. jdbc.queryForObject(...)!!).

P.S.: it is quite common to get an empty response by id querying (e.g. an item by this id was deleted or so) and repositories often return nullable type or throw an exception in such a case, so personally I'd stick to this solution. But if your design guarantees that an item always be present when queried by id, I'd use the !! to cast nullable type to non-nullable by force.