https://kotlinlang.org logo
#random
Title
# random
j

janvladimirmostert

02/27/2024, 8:05 AM
is it somehow possible to overwrite the hashCode that's returned by
T::class.hashCode
where T is an interface (or abstract class) that I control?
Copy code
interface Foo {

}

inline fun <reified T : Foo> decode(byteArray: ByteArray) {
	println(T::class.hashCode())
	println(T::class.java.hashCode())
}

class INT4 : Foo {
	companion object : Foo
}
s

Sam

02/27/2024, 8:36 AM
No. The hash code of a
Class
or
KClass
is just the Java class's identity hash code, which is probably based on its location in memory.
Why would you want to change it?
j

janvladimirmostert

02/27/2024, 9:27 AM
thanks, I thought as much. related to https://stackoverflow.com/questions/78064180/get-value-from-reified-generic-without-using-reflection#78064251 I'm trying to map a generic back to an instance without using reflection
s

Sam

02/27/2024, 10:22 AM
So basically you're trying to get from a type to its companion object instance? Or even more basically, you're looking for a way to associate behaviour/information dynamically with a type, without having an actual instance of that type.
I think the answer in the SO thread is about as good as you're going to get. You might be able to optimise it a bit using
ClassValue
, which is the canonical Java way to store a mapping between a class and a value, and should be pretty fast and efficient.
But do you really need to do this? If you're using reified generics, that means there's some place in the code where you reference the exact class by name anyway. Why not just reference the companion object instead? i.e.
decode(INT4, someEncodedByteArray)
instead of
decode<INT4>(someEncodedByteArray)
.
If you need some extra type information to go with it, you can always add some generic parameters to the
Decodable
interface itself
j

janvladimirmostert

02/27/2024, 12:02 PM
decode<T1>(data) calls docode1(data, types=listOf(T1)), decode<T1,....T42>(data) calls decode42(data, types=listeOf(T1, T2, ...T42) plus all the other special Tuple1-42 classes that automatically gets inferred thanks to the type being a generic instead of a value. it works great, except for that reflection performance penalty. I've never seen ClassValue before, but wouldn't I still need to create a manual mapping somehow or am missing something?
Copy code
object Mapper : ClassValue<Int>() {
	override fun computeValue(type: Class<*>?): Int {
		TODO("lookup type somehow")
	}
}

inline fun <reified T : Foo> decode(byteArray: ByteArray) {
	println(Mapper.get(T::class.java))
}
ah, I get it now; the purpose of ClassValue is to translate a class to some other value you can use in a map so you don't interfere with garbage collection while caching this lookup somehow: https://medium.com/@mostekdominik/classes-as-map-keys-in-java-481bea29fd65 and that computeValue is only called once, thereafter that is cached automatically until you call remove. thanks for this interesting info!!
reflectionless prototype
Copy code
interface Enc {}
interface Dec {
	val code: Int
}

object Mapper : ClassValue<Int>() {
	private val mappings = listOf(INT4, FLOAT)
	override fun computeValue(type: Class<*>?): Int {
		println("computing value for $type")
		for (mapping in mappings) {
			val matches = (mapping::class.qualifiedName?.startsWith(type?.canonicalName ?: "?")) == true
			if (matches) {
				return mapping.code
			}
		}
		return -1
	}
}

inline fun <reified T : Enc> decode(byteArray: ByteArray) {
	println(Mapper.get(T::class.java))
}

class INT4 : Enc {
	companion object : Dec {
		override val code: Int
			get() = 1
	}
}

class FLOAT : Enc {
	companion object : Dec {
		override val code: Int
			get() = 2

	}
}

fun main() {
	decode<INT4>(byteArrayOf()) // computeValue called
	decode<INT4>(byteArrayOf())
	decode<INT4>(byteArrayOf())
	decode<INT4>(byteArrayOf())
	decode<INT4>(byteArrayOf())
	decode<FLOAT>(byteArrayOf()) // computeValue called
}
I'll mess a around a bit more, but this is decent, that compute is only called once which is perfect