I am facing some very basic problem (that never faced in java before) and might be due my lack of knowledge in Kotlin.
I am currently trying to read a YML file. So Im doing it in this way:
private val factory = YamlConfigurationFactory(LinkedHashMap::class.java, validator, objectMapper, "dw")
Best on Dropwizard guide for configurations.
https://www.dropwizard.io/1.3.12/docs/manual/testing.html
So later in my function I do this"
val yml = File(Paths.get("config.yml").toUri())
var keyValues = factory.build(yml)
When using my debugger I can see there is a Map with key->values, just as it should be.
now when I do keyValues.get("my-key")
type inference failed. the value of the type parameter k should be mentioned in input types
Tried this but no luck
var keyValues = LinkedHashMap<String, Any>()
keyValues = factory.build(yml)
The YamlConfigurationFactory requires a class to map to, but I dont know if there is a more direct way to specify a Kotlin class than with the current solution +.kotlin, like
LinkedHashMap::class.java.kotlin
Here it also throws an error.
Ideas?
Well, this is a typical problem with JVM generics. Class<LinkedHashMap> carries no info on what are the actual types of its keys and values, so the keyValues variable always ends up with the type LinkedHashMap<*, *> simply because it can't be checked at compile time. There are two ways around this:
Unsafe Cast
This is how you would deal with the problem in standard Java: just cast the LinkedHashMap<*, *> to LinkedHashMap<String, Any> (or whatever is the actual expected type). This produces a warning because the compiler can't verify the cast is safe, but it is also generally known such situations are often unavoidable when dealing with JVM generics and serialisation.
YamlConfigurationFactory(LinkedHashMap::class.java, ...) as LinkedHashMap<String, Any>
Type Inference Magic
When using Kotlin, you can avoid the cast by actually creating instance of Class<LinkedHashMap<String, Any>> explicitly. Of course, since this is still JVM, you lose all the type info at runtime, but it should be enough to tell the type inference engine what your result should be. However, you'll need a special helper method for this (or at least I haven't found a simpler solution yet), but that method needs to be declared just once somewhere in your project:
inline fun <reified T> classOf(): Class<T> = T::class.java
...
val factory = YamlConfigurationFactory(classOf<LinkedHashMap<String, Any>>(), ...)
Using this "hack", you'll get an instance of LinkedHashMap directly, however, always remember that this is just extra info for the type inference engine but effectively it just hides the unsafe cast. Also, you can't use this if the type is not known at compile type (reified).
Related
I don't or can't modify the Java source code. The goal to configure just the Kotlin compiler to know what is nullable and what isn't.
You can specify the type manually if you know something will never be null. For example, if you have the following Java code:
public static Foo test() {
return null;
}
and you call it in Kotlin like this:
val result = Foo.test()
then result will have a type of Foo! by default – which means it can be either Foo or Foo?.. the compiler doesn't have enough information to determine that.
However, you can force the type manually:
val result: Foo = Foo.test()
// use "result" as a non-nullable type
Of course, if at runtime that is not true, you'll get a NullPointerException.
For reference, please check the documentation.
I don't know of a way to configure the compiler for this, but IntelliJ IDEA has a feature that allows you to add annotations to code via an XML file called external annotations.
You can add the Jetbrains #Nullable and #NotNull annotations to library code, but when I've tried it, it only results in compiler warnings rather than errors when you use incorrect nullability in your code. These same annotations generate compiler errors when used directly in the source code. I don't know why there is a difference in behavior.
You can use extension functions for this. If you have a method String foo() in the class Test, you can define the extension function
fun Test.safeFoo(): String = this.foo()!!
The advantage is that the code is pretty obious.
The disadvantage of this approach is that you need to write a lot of boiler plate code. You also have to define the extension function in a place where all your modules or projects can see it. Also, writing that much code just to avoid !! feels like overkill.
It should also be possible to write a Kotlin compiler extension which generates them for you but the extension would need to know which methods never return null.
class Example(private val childrenByParent: HashMap<String, List<String>>) {
private val parents: List<String> = childrenByParent.keys.toList()
fun getChildrenCount(parentPosition: Int): Int {
return childrenByParent[parents[parentPosition]].size
// error, recommends using "?." or "!!"
}
}
The compiler won't let me call size directly but I don't understand why. There are no nullable types in sight.
If I let the compiler infer the type by doing this:
val infer = childrenByParent[parents[parentPosition]]
I can see that it assumes it's a List<String>?
It seems that I'm quite confused about nullability still. Would appreciate some help. I have a feeling I'm doing something incredibly dumb, but after some searching and testing I failed at fixing this.
I would like for this function to not use ?. or even worse, !!. Is it possible? At least, using HashMap and List<String>.
HashMap.get(Object) returns null when there is no element matching the key you provided, so its return type is effectively nullable, regardless of whether the values are or not.
So unfortunately you have to account for the case in which the key doesn't exist, so your choices are either implementing a case where it doesn't, or just declaring it as non-null with !! if you are sure the key exists.
Otherwise you can use HashMap.containsKey(String) to ensure the key exists and then you can be confident that using !! on the value won't result in a NullPointerException.
However as #gidds pointed out, this is not naturally thread-safe without some more work, so it might be best to just handle the case of the key not being in the map. Also I cannot actually think of many cases where you could be sure that key exists, in which a Map is the most appropriate data structure to use.
Also, even though this is not the case here, remember that nullability is just a feature of Kotlin, so when using some classes originally written in Java, whether an element is nullable or not is unknown. The IDE will usually represent this as Type! where the single ! tells you it is a platform type.
I tried to implement some type classes from Haskell but confronted the issue that is probably a bug in the Kotlin compiler.
interface Semigroup<Instance> {
infix fun Instance.assocOp(oother: Instance): Instance
}
inline fun <reified T: Semigroup<T>> Iterable<T>.concat() = this.reduce<T, T> { acc: T, t: T -> acc.assocOp(t) }
The error message is "Expected parameter of type T".
IDEA suggests to "Change type from 'T' to 'T'" (does nothing).
I expect acc to belong to the type T mentioned in generics. But because of some reason compiler tries to find some other type T. I tried to
specify the type explicitly/implicitly
build ignoring IDEA message
change used version of Kotlin compiler (I have tried 1.4.20, 1.4.10, 1.3.72).
Nothing worked.
I suppose that writing the function without reduce (manually) may help to deal with it. Also, writing java code doing the same may help to mitigate the problem. But these solutions are only workarounds for the problem. Is the issue my fault or the compiler bug?
The compiler error clearly is not helpful here. However, it is correct that the code should not compile IMO.
You're defining the method assocOp as a member extension function. The extension applies to any type T, but it's a member of the interface Semigroup<T>.
To call that extension, you need both a receiver or type T and a receiver of type Semigroup<T> (acting as a context).
In your case, the type T both plays the role of the generic type parameter and of the Semigroup<T>, but you still need to have 2 "receivers" for your extension, even if both are the same instance.
Maybe try this:
inline fun <reified T : Semigroup<T>> Iterable<T>.concat(): T =
reduce<T, T> { t1: T, t2: T -> with(t1) { t1.assocOp(t2) } }
The with(t1) { ... } provides a context of type Semigroup<T>, while the t1 used in t1.assocOp(t2) acts as the T receiver.
Is there a way to enforce 1..* parameters in Kotlin that will still allow the spread operator?
I've tried:
class Permission(
// 1..n compliance
accessiblePage: Webpage,
vararg accessiblePages: Webpage
) {
And that does enforce 1..*, but it also means that Permission(*pages) won't work, so that's a pretty awkward interface.
Is there an easy way to enforce 1..* without a runtime constructor error?
There is, unfortunately, no way to check this in Kotlin at compile time aside from the way you mentioned. Since vararg parameters are really just syntactic sugar for an array, your code is essentially
class Permission (
accessiblePage: Webpage,
accessiblePages: Array<Webpage>
)
So the question then becomes "Can you ensure that an array has at least one element in it at compile time?" For most languages, that's a clear no, although the Kotlin team did at one point experiment with it:
[C]urrently, Kotlin compiler doesn't collect static information about
collections size. FYI, at some point Kotlin team tried to collect such
information and use it for warnings about possible
IndexOutOfBoundException and stuff like that, but it was found that
there were a very little demand on such diagnostics in real-life
projects, so, given complexity of such analysis, it was abandoned[.]
(https://github.com/Kotlin/KEEP/issues/139#issuecomment-405551324)
It's possible that this metadata will be added at some point, but you shouldn't expect it soon.
That said, you could always combine a runtime check in the case of an Array with an overloaded signature in the case of varargs. This would mean that your vararg example would work the same, but passing an array to the function would subject it to a runtime check (you'd also not have to use the spread operator anymore):
class Permission (
accessiblePage: Webpage
vararg accessiblePages: Webpage
) {
constructor(accessiblePages: Array<Webpage>) {
require(accessiblePages.isNotEmpty()) {
"Must have at least one accessible page."
}
}
}
called like
val permission1 = Permission(Webpage(), Webpage())
val permission2 = Permission() // Would fail at compile time
val pages = arrayOf()
val permission3 = Permission(pages) // Would fail at runtime. Note also the lack of the spread operator.
In my Kotlin code, I have a variable that is the Type interface from
java.lang.reflect
var type: Type
But I need to cast this to:
List<UserInfo>
If I was not casting to a List, I would just do this:
var type = UserInfo::class.java
and this works. But I don't know how to cast it using a List. The closest I found is this:
var type = Array<UserInfo>::class.java
This would compile if my UserInfo was an Array but it's a List.
The issue (as #Miha_x64 says) is type erasure.
The Java Virtual Machine knows nothing about type parameters. So although the source specified a List<UserInfo>, it compiles down to a plain List.
So this works:
var type = List::class.java
(Your Array example works because arrays are a special case: they're directly supported in the JVM, and keep their types at runtime.)
Java's use of type erasure is at least partly for historical reasons; when generics were added to Java 5, they wanted to preserve compatibility with existing source and bytecode. For all the gory details (much of which is inherited by Kotlin), see here.
Maybe its late. Try to use KTypeProjection and createType() with KClass instead. Then get the value in KType or convert it back to java Type.
val kClass = YourClass::class
val kTypeProjection = KTypeProjection.invariant(entity.starProjectedType)
val kType = List::class.createType(listOf(kTypeProjection))
val type = kType.javaType
result:
kType: kotlin.collections.List<YourClass>
type: java.util.List<YourClass>