Infer Kotlin null-safety from type variables

This commit removes the constraint from type variables in
PropertyResolver, JdbcOperations and RestOperations
Kotlin extensions in order to get null-safety inferred
from the type declared by the user.

Closes gh-22687
This commit is contained in:
Sebastien Deleuze
2019-03-27 08:50:42 +01:00
parent 68a529b915
commit cbb5a78aa0
6 changed files with 175 additions and 124 deletions

View File

@@ -24,8 +24,8 @@ import java.sql.ResultSet
* @author Mario Arias
* @since 5.0
*/
inline fun <reified T : Any> JdbcOperations.queryForObject(sql: String): T? =
queryForObject(sql, T::class.java)
inline fun <reified T> JdbcOperations.queryForObject(sql: String): T =
queryForObject(sql, T::class.java) as T
/**
* Extensions for [JdbcOperations.queryForObject] providing a RowMapper-like function
@@ -34,8 +34,8 @@ inline fun <reified T : Any> JdbcOperations.queryForObject(sql: String): T? =
* @author Mario Arias
* @since 5.0
*/
fun <T : Any?> JdbcOperations.queryForObject(sql: String, vararg args: Any, function: (ResultSet, Int) -> T): T? =
queryForObject(sql, RowMapper { resultSet, i -> function(resultSet, i) }, *args)
inline fun <reified T> JdbcOperations.queryForObject(sql: String, vararg args: Any, crossinline function: (ResultSet, Int) -> T): T =
queryForObject(sql, RowMapper { resultSet, i -> function(resultSet, i) }, *args) as T
/**
* Extension for [JdbcOperations.queryForObject] providing a
@@ -44,8 +44,8 @@ fun <T : Any?> JdbcOperations.queryForObject(sql: String, vararg args: Any, func
* @author Mario Arias
* @since 5.0
*/
inline fun <reified T : Any> JdbcOperations.queryForObject(sql: String, args: Array<out Any>, argTypes: IntArray): T? =
queryForObject(sql, args, argTypes, T::class.java)
inline fun <reified T> JdbcOperations.queryForObject(sql: String, args: Array<out Any>, argTypes: IntArray): T? =
queryForObject(sql, args, argTypes, T::class.java) as T
/**
* Extension for [JdbcOperations.queryForObject] providing a
@@ -54,8 +54,8 @@ inline fun <reified T : Any> JdbcOperations.queryForObject(sql: String, args: Ar
* @author Mario Arias
* @since 5.0
*/
inline fun <reified T : Any> JdbcOperations.queryForObject(sql: String, args: Array<out Any>): T? =
queryForObject(sql, args, T::class.java)
inline fun <reified T> JdbcOperations.queryForObject(sql: String, args: Array<out Any>): T? =
queryForObject(sql, args, T::class.java) as T
/**
* Extension for [JdbcOperations.queryForList] providing a `queryForList<Foo>("...")` variant.
@@ -64,7 +64,7 @@ inline fun <reified T : Any> JdbcOperations.queryForObject(sql: String, args: Ar
* @since 5.0
*/
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
inline fun <reified T : Any> JdbcOperations.queryForList(sql: String): List<T> =
inline fun <reified T> JdbcOperations.queryForList(sql: String): List<T> =
queryForList(sql, T::class.java)
/**
@@ -75,7 +75,7 @@ inline fun <reified T : Any> JdbcOperations.queryForList(sql: String): List<T> =
* @since 5.0
*/
@Suppress("EXTENSION_SHADOWED_BY_MEMBER")
inline fun <reified T : Any> JdbcOperations.queryForList(sql: String, args: Array<out Any>,
inline fun <reified T> JdbcOperations.queryForList(sql: String, args: Array<out Any>,
argTypes: IntArray): List<T> =
queryForList(sql, args, argTypes, T::class.java)
@@ -86,7 +86,7 @@ inline fun <reified T : Any> JdbcOperations.queryForList(sql: String, args: Arra
* @author Mario Arias
* @since 5.0
*/
inline fun <reified T : Any> JdbcOperations.queryForList(sql: String, args: Array<out Any>): List<T> =
inline fun <reified T> JdbcOperations.queryForList(sql: String, args: Array<out Any>): List<T> =
queryForList(sql, args, T::class.java)
/**
@@ -96,9 +96,9 @@ inline fun <reified T : Any> JdbcOperations.queryForList(sql: String, args: Arra
* @author Mario Arias
* @since 5.0
*/
inline fun <reified T : Any?> JdbcOperations.query(sql: String, vararg args: Any,
crossinline function: (ResultSet) -> T): T? =
query(sql, ResultSetExtractor { function(it) }, *args)
inline fun <reified T> JdbcOperations.query(sql: String, vararg args: Any,
crossinline function: (ResultSet) -> T): T =
query(sql, ResultSetExtractor { function(it) }, *args) as T
/**
* Extension for [JdbcOperations.query] providing a RowCallbackHandler-like function
@@ -117,5 +117,5 @@ fun JdbcOperations.query(sql: String, vararg args: Any, function: (ResultSet) ->
* @author Mario Arias
* @since 5.0
*/
fun <T : Any> JdbcOperations.query(sql: String, vararg args: Any, function: (ResultSet, Int) -> T): List<T> =
fun <T> JdbcOperations.query(sql: String, vararg args: Any, function: (ResultSet, Int) -> T): List<T> =
query(sql, RowMapper { rs, i -> function(rs, i) }, *args)

View File

@@ -16,9 +16,11 @@
package org.springframework.jdbc.core
import io.mockk.every
import io.mockk.mockk
import io.mockk.verify
import org.junit.Assert.assertEquals
import org.junit.Assert.assertNull
import org.junit.Test
import java.sql.*
@@ -30,91 +32,96 @@ import java.sql.*
*/
class JdbcOperationsExtensionsTests {
val template = mockk<JdbcTemplate>(relaxed = true)
val template = mockk<JdbcTemplate>()
val sql = "select age from customer where id = 3"
@Test
fun `queryForObject with reified type parameters`() {
val sql = "select age from customer where id = 3"
template.queryForObject<Int>(sql)
verify { template.queryForObject(sql, Integer::class.java) }
every { template.queryForObject(sql, any<Class<Int>>()) } returns 2
assertEquals(2, template.queryForObject<Int>(sql))
verify { template.queryForObject(sql, any<Class<Int>>()) }
}
@Test
fun `queryForObject with RowMapper-like function`() {
val sql = "select age from customer where id = ?"
template.queryForObject(sql, 3) { rs: ResultSet, _: Int -> rs.getInt(1) }
every { template.queryForObject(sql, any<RowMapper<Int>>(), any<Int>()) } returns 2
assertEquals(2, template.queryForObject(sql, 3) { rs: ResultSet, _: Int -> rs.getInt(1) })
verify { template.queryForObject(eq(sql), any<RowMapper<Int>>(), eq(3)) }
}
@Test // gh-22682
fun `queryForObject with nullable RowMapper-like function`() {
val sql = "select age from customer where id = ?"
template.queryForObject(sql, 3) { _, _ -> null as Int? }
every { template.queryForObject(sql, any<RowMapper<Int>>(), 3) } returns null
assertNull(template.queryForObject(sql, 3) { _, _ -> null as Int? })
verify { template.queryForObject(eq(sql), any<RowMapper<Int?>>(), eq(3)) }
}
@Test
fun `queryForObject with reified type parameters and argTypes`() {
val sql = "select age from customer where id = ?"
val args = arrayOf(3)
val argTypes = intArrayOf(JDBCType.INTEGER.vendorTypeNumber)
template.queryForObject<Int>(sql, args, argTypes)
verify { template.queryForObject(sql, args, argTypes, Integer::class.java) }
every { template.queryForObject(sql, args, argTypes, any<Class<Int>>()) } returns 2
assertEquals(2, template.queryForObject<Int>(sql, args, argTypes))
verify { template.queryForObject(sql, args, argTypes, any<Class<Int>>()) }
}
@Test
fun `queryForObject with reified type parameters and args`() {
val sql = "select age from customer where id = ?"
val args = arrayOf(3)
template.queryForObject<Int>(sql, args)
verify { template.queryForObject(sql, args, Integer::class.java) }
every { template.queryForObject(sql, args, any<Class<Int>>()) } returns 2
assertEquals(2, template.queryForObject<Int>(sql, args))
verify { template.queryForObject(sql, args, any<Class<Int>>()) }
}
@Test
fun `queryForList with reified type parameters`() {
val sql = "select age from customer where id = 3"
template.queryForList<Int>(sql)
verify { template.queryForList(sql, Integer::class.java) }
val list = listOf(1, 2, 3)
every { template.queryForList(sql, any<Class<Int>>()) } returns list
assertEquals(list, template.queryForList<Int>(sql))
verify { template.queryForList(sql, any<Class<Int>>()) }
}
@Test
fun `queryForList with reified type parameters and argTypes`() {
val sql = "select age from customer where id = ?"
val list = listOf(1, 2, 3)
val args = arrayOf(3)
val argTypes = intArrayOf(JDBCType.INTEGER.vendorTypeNumber)
template.queryForList<Int>(sql, args, argTypes)
verify { template.queryForList(sql, args, argTypes, Integer::class.java) }
every { template.queryForList(sql, args, argTypes, any<Class<Int>>()) } returns list
assertEquals(list, template.queryForList<Int>(sql, args, argTypes))
verify { template.queryForList(sql, args, argTypes, any<Class<Int>>()) }
}
@Test
fun `queryForList with reified type parameters and args`() {
val sql = "select age from customer where id = ?"
val list = listOf(1, 2, 3)
val args = arrayOf(3)
every { template.queryForList(sql, args, any<Class<Int>>()) } returns list
template.queryForList<Int>(sql, args)
verify { template.queryForList(sql, args, Integer::class.java) }
verify { template.queryForList(sql, args, any<Class<Int>>()) }
}
@Test
fun `query with ResultSetExtractor-like function`() {
val sql = "select age from customer where id = ?"
template.query<Int>(sql, 3) { rs ->
every { template.query(eq(sql), any<ResultSetExtractor<Int>>(), eq(3)) } returns 2
assertEquals(2, template.query<Int>(sql, 3) { rs ->
rs.next()
rs.getInt(1)
}
})
verify { template.query(eq(sql), any<ResultSetExtractor<Int>>(), eq(3)) }
}
@Test // gh-22682
fun `query with nullable ResultSetExtractor-like function`() {
val sql = "select age from customer where id = ?"
template.query<Int?>(sql, 3) { _ -> null }
every { template.query(eq(sql), any<ResultSetExtractor<Int?>>(), eq(3)) } returns null
assertNull(template.query<Int?>(sql, 3) { _ -> null })
verify { template.query(eq(sql), any<ResultSetExtractor<Int?>>(), eq(3)) }
}
@Suppress("RemoveExplicitTypeArguments")
@Test
fun `query with RowCallbackHandler-like function`() {
val sql = "select age from customer where id = ?"
every { template.query(sql, ofType<RowCallbackHandler>(), 3) } returns Unit
template.query(sql, 3) { rs ->
assertEquals(22, rs.getInt(1))
}
@@ -123,10 +130,11 @@ class JdbcOperationsExtensionsTests {
@Test
fun `query with RowMapper-like function`() {
val sql = "select age from customer where id = ?"
template.query(sql, 3) { rs, _ ->
val list = listOf(1, 2, 3)
every { template.query(sql, ofType<RowMapper<*>>(), 3) } returns list
assertEquals(list, template.query(sql, 3) { rs, _ ->
rs.getInt(1)
}
})
verify { template.query(sql, ofType<RowMapper<*>>(), 3) }
}