kotlin: wrong inference of nullability in function reference? - kotlin

i want to choose a validation function for a nullable string:
val aString: String? = ...
val validation
// : KFunction1<CharSequence?, Boolean>
= if (...) Objects::isNull else StringUtils::isNotBlank
isNotBlank is from apache commons. it takes string and returns bool.
when i uncomment explicit type declaration all is good. but when there is no explicit type declared, it infers KFunction1<CharSequence, Boolean> so parameter cannot be null. why?

This is intended behavior. The type to the left of :: is actually the type of the instance you're going to pass to the reference as the first argument, so type inference uses it as the first argument to KFunction1.
If you'd like a reference to be callable on StringUtils? objects, use StringUtils?::isNotBlank, but the StringUtils::isNotBlank reference will only accept non-null StringUtils objects.
Reference

Related

How to access the field of top-level object?

When I do this
val data = object {
val field = 5
}
fun main(){
println(data.field) // throws
}
It throws Unresolved reference: field.
But all of this is ok:
val field = 6
class Data(val field: Int = 7)
val data7 = Data()
fun main(){
val data4 = object {
val field = 4
}
println(field) // ok
println(data4.field) // ok
println(data7.field) // ok
}
I do not get it, why Kotlin does not let me use properties from top-level objects? I thought that object is just like class object, but anonymous (without class) and there should be no difference between data and data7 in examples above. But it seems that there is difference.
This is documented in the "Object Literals" section of the Language Specification, about the difference between object declarations and anonymous objects (the things that object literals create).
The main difference between a regular object declaration and an anonymous object is its type. The type of an anonymous object is a special kind of type which is usable (and visible) only in the scope where it is declared. It is similar to a type of a regular object declaration, but, as it cannot be used outside the declaring scope, has some interesting effects.
Your data here is considered to have escaped the declaring scope of "the top level of the file", because it is public. You can access it from the top level scopes of other files.
Note: in this context “escaping current scope” is performed immediately if the corresponding value is declared as a non-private global- or classifier-scope property, as those are parts of an externally accessible interface.
Marking it private would have fixed it. The reason for the error is that:
When a value of an anonymous object type escapes current scope:
If the type has only one declared supertype, it is implicitly downcasted to this declared supertype;
If the type has several declared supertypes, there must be an implicit or explicit cast to any suitable type visible outside the scope, otherwise it is a compile-time error.
Here, the super type is implicitly Any, so the type of data is Any, and obviously there is no field on the type Any.
On the other hand, data4 have not escaped the current scope, because it is local to the main function's statement scope. You can't access it from another scope.
See also the great example from the spec.

ReflectionHelper::assertGetterMethod() conflicts with Construction Heuristics requirement

I have the following:
#CustomShadowVariable(variableListenerRef = #PlanningVariableReference(variableName = "..."))
#Override
public boolean isXyz() {
return xyz;
}
Then I get the following:
java.lang.IllegalStateException: The entityClass (class ...) has a PlanningVariable annotated member (bean property ... on class ...) that returns a primitive type (boolean). This means it cannot represent an uninitialized variable as null and the Construction Heuristics think it's already initialized.
Maybe let the member (...) return its primitive wrapper type instead.
... see I really would change boolean to Boolean, but then I'll get the following:
java.lang.IllegalStateException: The getterMethod (public java.lang.Boolean isXyz()) with a CustomShadowVariable annotation must have a primitive boolean return type (class java.lang.Boolean) or use another prefix in its methodName (isXyz).
It seems that the Construction Heuristics wants a nullable type so it can have a null initialization value, yet when provided with a Boolean object, the reflection helper assertion tries to validate it against the primitive type which fails.
Is this a defect?
Changing the method name to getXyz (full signature: Boolean getXyz()).
I'm only guessing because I don't have an example domain containing such shadow variable at hand. But looking at the ReflectionHelper code, this could be the solution. If that works, I'll try to improve the exception message.

Non-null type in nullable references

I have a class for binding a viewholder
class ViewHolderBinder(val onBind: (ViewHolder) -> Unit, val onClick: () -> Unit)
There is a list of these items in presenter
val items: MutableList<ViewHolderBinder> = mutableListOf()
ViewHolderBinder contains 2 non-null functions, but this call causes compile-time error
items.getOrNull(position)?.onClick()
However this call compiles as expected
items.getOrNull(position)?.let { it.onClick() }
Maybe i have missed something, but 2 these constructions are fully equivalent and i prefer to use first one, but it is not compiling.
I am using kotlin 1.3.10
Here's the actual error message you're getting:
Reference has a nullable type '(() -> Unit)?', use explicit '?.invoke()' to make a function-like call instead
Suppose you had this code:
val binder: ViewHolderBinder? = getBinder()
binder?.onClick()
onClick() is not a function you can invoke on the ViewHolderBinder instance. It's a property that holds a callback object. The full syntax to invoke that callback's function is
binder?.onClick?.invoke()
Kotlin also offers a special shorthand syntax that would work on a non-nullable binder:
binder.onClick()
If you apply it to a nullable binder,
binder?.onClick()
it expands to
binder?.onClick.invoke()
The type of the expression binder?.onClick is (() -> Unit)?, just like the error says. You aren't allowed to apply the . operator to a nullable type.

How to use type check in Kotlin?

In Kotlin doc, type check use is but when I write this code
var a="hello"
if (a is String) print(a)
There is a warning
warning: check for instance is always 'true'
if (a is String) print(a)
^
Thank you very much for all answers.
In your example, "hello" is a String literal. In Kotlin, even when you omit the type for a variable, its type is inferred. The compiler infers the type for var a from the initializer expression, and so the type of a is String. The warning you are getting means that the expression a that you check is always a String.
Your variable declaration is equivalent to var a: String = "hello", i.e. the variable may only reference a String, assigning any other type is not allowed.
For example, if you change the variable declaration to var a: Any = "hello", there will be no warning since the variable now may hold an instance of any type, not just a String.
I just figured out how to use type check by learning from Swift
open class fruit {}
class banana: fruit() {}
fun test( a: fruit ) {
if (a is banana) print("ok")
}
test(banana())

HashMap errors - containsKey, get

Can anyone shed some light?
Problem code:
protected var table = HashMap<Class<*>, Double>()
if (table.containsKey(object)) {
value = table.get(object)
}
containsKey(K):Boolean is deprecated. Map and Key have incompatible
types. upcast to Any? if you're sure
so I changed it to this:
if (table.containsKey(object as Any?)
which fixes the error, but is this what I should have done to fix it? or is there a better way?
also .get(object) has an error:
Type inference failed. required: kotlin.Double found kotlin.Double?
same error message for this too:
val c = someObject.javaClass // pre j2k code: final Class<? extends SomeClass> c = someObject.getClass();
weight = weightingTable[c] <-- error here
I don't know what to do here
The containsKey call is reported as an error because the type of the argument you pass to it does not match the type of the map key. Your map contains classes as keys, and you're trying to pass an object instance to it. Changing this to object as Any? is not a useful fix, because this call will compile but will always return false. What you need to do instead is to use object.javaClass to get the class of the object.
The weightingTable[c] call is reported as an error because the map does not necessarily contain a value for the key you're passing to it, so the result of the [] operation is nullable. You cannot assign a nullable value to a non-null variable without somehow handling the null case (using a check, an explicit non-null cast or some other option covered in the documentation).
When doing:
myHashMap.get(object)
and getting:
Type inference failed. required: kotlin.Double found kotlin.Double?
even when you already checked with containsKey. You can solve it by using:
myHashMap.getOrElse(key) { defaultValue }