I am porting my template code to XTend. At some point I have this type of condition handling in a test case:
#Test
def xtendIfTest() {
val obj = new FD
if (true && obj?.property?.isNotNull) {
return
}
fail("Not passed")
}
def boolean isNotNull(Object o) {
return o != null
}
class FD {
#Accessors
String property
}
This works as expected as the property is null and the test will fail with "Not passed" message. But a simple change in the return type of isNotNull method to Boolean (wrapper):
def Boolean isNotNull(Object o) {
return o != null
}
fails with a NullPointerException. Examining the generated java code for this I can see that XTend is using an intermediate Boolean object expression and that is the cause of NPE. Am I missing the point of the XTend null safe operator (?.) or I can't use a method like this after the operator?
Thanks.
The operator behaves properly. The exception is thrown because of the usage of a Boolean in an if-expression, which requires auto-unboxing.
If you try the following:
#Test
def xtendIfTest() {
val Boolean obj = null
if (obj) {
return
}
fail("Not passed")
}
You will also run into a NullPointerException.
This is consistent with the Java Language Specification (https://docs.oracle.com/javase/specs/jls/se7/html/jls-5.html#jls-5.1.8) - when auto-unboxing is required this can yield a NullPointerException:
#Test
public void test() {
Boolean value = null;
if (value) { // warning: Null pointer access: This expression of type Boolean is null but requires auto-unboxing
// dead code
}
}
Hope that helps.
Short answer: Change the second null-safe call to a regular call.
I.e. change
obj?.property?.isNotNull
to this:
obj?.property.isNotNull
Long answer:
The docs describe the null-safe operator thusly:
In many situations it is ok for an expression to return null if a
receiver was null
That means the second call in your example, property?. won't even call isNotNull if the left side of the call was null. Instead, it will return null. So the conditional "effectively" evaluates to:
if (true && null) { // causes NPE when java tries to unbox the Boolean
(By the way - the true is superfluous in this context, but I'm keeping it just in case you had another condition to check - I'm assuming you're just simplifying it to true for this example.)
If you make the change I'm suggesting, obj?.property will be evaluated, then the result will be passed to isNotNull, evaluating to this:
if (true && isNotNull(null)) {
which returns the proper Boolean object that will be auto-unboxed as expected.
A Word of Caution
In your first form of isNotNull, i.e. the one returning primitive boolean, you should actually get a warning like "Null-safe call of primitive-valued feature isNotNull, default value false will be used".
This is because you're stretching the intent of the null-safe call, which is to return null without invoking the right side method if the left side of the operator was null. But if your isNotNull returns a primitive boolean, the whole expression obviously can't evaluate to null, so Xtend uses a default instead, which is false for booleans.
To emphasize the problem in a different way - it evaluates to false without calling isNotNull - that means even if you used a method isNull after the operator, it would still return false!
The docs also mention this behavior (albeit in general terms):
For primitive types the default value is returned (e.g. 0 for int).
This may not be what you want in some cases, so a warning will be
raised by default
So I recommend always using a non-primitive return value on the right-hand side of a null-safe call. But if you're going to convert the isNotNull to a regular call as I suggested, this rule doesn't apply, and either return type is fine.
Related
Look at the following code running in Kotlin REPL:
>>> null.takeIf({1==2})
res4: kotlin.Nothing? = null
Why this don't cause NullPointerException?
You code doesn't cause NullPointerException because takeIf is an extension function.
Extension functions are translated into method receiver, first argument being the object you invoked your function on.
So instead of null.takeIf { false } (simplifying your example), you could wonder why the following function doesn't throw a NullPointerException:
fun <T> takeIf(me: T, predicate: (T) -> Boolean): T? {
...
}
Well, for that we need to look into takeIf() implementation (removing annotations and contract):
public inline fun <T> T.takeIf(predicate: (T) -> Boolean): T? {
return if (predicate(this)) this else null
}
So, in case your predicate is false (as in your example, 1==2), it just short-circuits and returns null.
What if it's true, then? Will we finally get a NullPointer?
Nope:
val nothing: Nothing? = null.takeIf {
true
}
Kotlin design approach for types aims to eliminate NPEs on purpose. Conditions that might trigger one are limited to, as seen on the docs:
An explicit call to throw NullPointerException();
Usage of the !! operator that is described below;
Some data inconsistency with regard to initialization, such as when:
An uninitialized this available in a constructor is passed and used somewhere ("leaking this");
A superclass constructor calls an open member whose implementation in the derived class uses uninitialized state;
Java interoperation:
Attempts to access a member on a null reference of a platform type;
Generic types used for Java interoperation with incorrect nullability, e.g. a piece of Java code might add null into a Kotlin MutableList, meaning that MutableList should be used for working with it;
Other issues caused by external Java code.
The third option is for NPE-lovers: the not-null assertion operator
(!!) converts any value to a non-null type and throws an exception if
the value is null. We can write b!!, and this will return a non-null
value of b (e.g., a String in our example) or throw an NPE if b is
null:
val l = b!!.length
Thus, if you want an NPE, you can have it, but you
have to ask for it explicitly, and it does not appear out of the blue.
So your code is behaving as expected. See the link for more info
https://kotlinlang.org/docs/reference/null-safety.html
I have a function as below.
fun process(string: String?): Int {
if (string != null) {
return string.filter { it.equals("a") }.length
}
return 0
}
It shows that it.equals("a") could be improved, with the message
Call replaceable with binary operator
So I just use Alt-Enter to change it accordingly and get
fun process(string: String?): Int {
if (string != null) {
return string.filter { it == "a" }.length
}
return 0
}
Unfortunately now it error stating
Operator '==' cannot be applied to Char and String.
I assume this is a bug in the proposed optimization?
Just to be sure we're on the same page, it is a Char because the filter method that you use operates on the string as a sequence of characters.
Therefore, it.equals("a") is effectively comparing a Char with a String. This can never be true, because a string can never be equal to any character, even if it contains only one. So the code doesn't work in either case, unless you change your string "a" to the character 'a'.
Even if the operator == is compiled to the equivalent equals() method call, the compiler has additional checks in the operator form compared to explicit method calls. The error you get is because the == operator requires a type match, as opposed to its method counterpart equals(). (Since "a" is a string, you can't use the operator to compare it with the character it).
Why the difference? You may ask.
As far as I understood, the Kotlin team followed the Java convention for the method equals and left its contract permissive by allowing Any? as parameter. This is probably because of constraints of interoperability with Java.
However, they still saved us from this kind of mistakes with the additional type safety of the operator.
I have to admit, though, that the IDE should give you a warning for your incorrect equals call before asking you to replace the method call by an operator.
With Kotlin 1.3 came a new feature, contracts, and with them the function require(), but it seems pretty similar to assert(). Here is what their KDoc says:
require(value: Boolean): Throws an IllegalArgumentException if the value is false.
assert(value: Boolean): Throws an AssertionError if the value is false and runtime assertions have been enabled on the JVM using the -ea JVM option.
So when should I use require() and when should I use assert()?
require and assert work differently. For this, you need to dive into the code.
assert(condition) calls a different method internally, which is where you see the actual code:
#kotlin.internal.InlineOnly
public inline fun assert(value: Boolean, lazyMessage: () -> Any) {
if (_Assertions.ENABLED) {
if (!value) {
val message = lazyMessage()
throw AssertionError(message)
}
}
}
AFAIK, this ties to the -ea flag; if -ea isn't present (or disabled), assert will not throw an exception.
As a result, this will not compile:
fun something(string: String?){
assert (string != null)
nonNull(string) // Type mismatch
}
fun nonNull(str: String){}
This is where require comes in. require(condition) also calls a different method under the hood. If you replace assert with require, you'll see that smart cast will successfully cast it as non-null, because require is guaranteed to throw an exception if the condition fails.
#kotlin.internal.InlineOnly
public inline fun require(value: Boolean, lazyMessage: () -> Any): Unit {
contract {
returns() implies value
}
if (!value) {
val message = lazyMessage()
throw IllegalArgumentException(message.toString())
}
}
The boolean-only function does the contract too, then calls that method if the contract fails.
Contracts are new, and I am not entirely sure how they work, but this is how I understand it:
The implies keyword is an infix fun; what this does is that it tells the compiler the condition is true if it returns from the method. This helps with auto-casting, like in the example I mentioned earlier. It doesn't actually cause the method to return (or at least that's what my current testing points to), but it's for the compiler.
It's also readable: returning implies condition is true
That's the contact part: the exception here is always thrown, as you can see by the condition. require uses if(!value), where as assert checks if(_Assertions.ENABLED && !value).
This isn't the only use for require though. It can also be used for validation of arguments. I.e. if you have this:
operator fun get(index: Int) : T {
if (index < 0 || index >= size)
throw IllegalArgumentException("Index out of range")
// return here
}
You could replace it with:
operator fun get(index: Int) : T {
require (index >= 0 && index < size) { "Index out of range" }
// return here
}
There are a lot different uses for this, but these are just some examples.
What this means:
assert is like in Java; it is only triggered if assertions are enabled. Using it does not guarantee the condition is met.
require always works, regardless of VM flags
Which means require can be used to help the compiler with i.e. smart cast, and it's better to use than assert for making sure arguments are valid. And since it also works regardless of VM flags, it can be used outside debugging cases, as forpas mentioned. If you're making a library written in Kotlin, you can replace argument checking with manual throwing with require, and it will still work. Obviously, this assumes Kotlin 1.3.0, but that's beside the point.
And it can be used internally to ensure smart cast works as expected, but throw an exception if the condition isn't met.
To answer your question though:
Use require when you want to to argument checking, even if it's in production.
Use assert if you're doing local debugging, and have the -ea flag enabled.
Let's say you want a function to calculate n! (factorial) like this:
fun factorial(n: Long): Long {
require(n >= 0) { "Number must not be negative" }
// code
}
In this case require() checks the validity of the argument passed to the function and throws an IllegalArgumentException if the argument is not what it's supposed to be and for debugging you also have the explanatory message.
On the other hand assert() can be used anywhere in your code to make your own specialized checks if runtime assertions have been enabled.
There is also
check(Boolean) throws IllegalStateException when its argument is false,
which is used to check object state.
So each of the above has its own place in your code and you can use it if you find it useful.
In lombok extension method obj.method() is a syntax sugar for SomeUtil.method(obj). It allows for obj be null.
Kotlin extensions methods are resolved statically so I assume it's the same syntactic sugar. But when I wrote
fun Any.stringOrNull() = this?.toString()
I got a warning about unnecessary safe call on non-null receiver. Does that mean I can't call extension functions on null objects like with Lombok?
You can call it on a nullable object if you define it to be an extension on a nullable type:
fun Any?.stringOrNull() = ...
Otherwise, like with any other method, you'd have to use the safe call operator.
You can create extensions on nullable receiver types. In your example, it has to be Any? instead of Any which would not allow null, see the docs:
Nullable Receiver
Note that extensions can be defined with a nullable receiver type. Such extensions can be called on an object variable even if its value is null, and can check for this == null inside the body. This is what allows you to call toString() in Kotlin without checking for null: the check happens inside the extension function.
fun Any?.toString(): String {
if (this == null) return "null"
// after the null check, 'this' is autocast to a non-null type, so the toString() below
// resolves to the member function of the Any class
return toString()
}
Be careful, for:
fun Any?.toString(): String
following behavior:
var obj: Any? = null
obj?.toString() // is actually null
obj.toString() // returns "null" string
just spent 15 very frustrating minutes before realized this...
val string: String? = "Hello World!"
print(string.length)
// Compile error: Can't directly access property of nullable type.
print(string?.length)
// Will print the string's length, or "null" if the string is null.
?. Safe Call operator for nullable receiver##
The safe call operator returns null if the value to the left is null, otherwise continues to evaluate the expression to the right, so in order to call any function on nullable receiver you need to use safe call operator after Any.(Use Any?)
Then you can check for null value of this(here this object points to receiver) inside function body.This is what allows you to call toString() in Kotlin without checking for null: the check happens inside the extension function.
fun Any?.toString(): String {
if (this == null) return "null"
// after the null check, 'this' is autocast to a non-null type, so the toString() below
// resolves to the member function of the Any class
return toString()
}
I have the following code fragment:
val foo: String? = null
foo.run { println("foo") }
I have here a nullable variable foo that is actually set to null followed by a nonsafe .run() call.
When I run the code snippet, I get foo printed out despite the fact that the run method is called on a null. Why is that? Why no NullPointerException? Why does compiler allow a nonsafe call on an optional value?
If I pass println(foo), I get a nice juicy null in the console so I think it's safe to assume that foo is actually null.
I believe, there are two things that both might be of some surprise: the language semantics that allow such a call, and what happens at runtime when this code executes.
From the language side, Kotlin allows nullable receiver, but only for extensions. To write an extension function that accepts a nullable receiver, one should either write the nullable type explicitly, or use a nullable upper bound for a type parameter (actually, when you specify no upper bound, the default one is nullable Any?):
fun List<*>?.isEmptyOrNull() = this == null || size == 0 // explicit nullable type
fun <T : CharSequence?> T.nullWhenEmpty() = if ("$this" == "") null else this // nullable T
fun <T> T.identity() = this // default upper bound Any? is nullable
This feature is used in kotlin-stdlib in several places: see CharSequence?.isNullOrEmpty(), CharSequence?.isNullOrBlank(), ?.orEmpty() for containers and String?.orEmpty(), and even Any?.toString(). Some functions like T.let, T.run that you asked about and some others just don't provide an upper bound for the type parameter, and that defaults to nullable Any?. And T.use provides a nullable upper bound Closeable?.
Under the hood, that is, from the runtime perspective, the extension calls are not compiled into the JVM member call instructions INVOKEVIRTUAL, INVOKEINTERFACE or INVOKESPECIAL (the JVM checks the first argument of such calls, the implicit this, for being null and throws an NPE if it is, and this is how Java & Kotlin member functions are called). Instead, the Kotlin extension functions are compiled down to static methods, and the receiver is just passed as the first argument. Such a method is called with the INVOKESTATIC instruction that does not check the arguments for being null.
Note that when a receiver of an extension can be nullable, Kotlin does not allow you to use it where a not-null value is required without checking it for null first:
fun Int?.foo() = this + 1 // error, + is not defined for nullable Int?
To add to what #holi-java said, there is nothing unsafe about your code at all. println("foo") is perfectly valid whether foo is null or not. If you tried something like
foo.run { subString(1) }
it would be unsafe, and you will find it won't even compile without some sort of null check:
foo.run { this?.subString(1) }
// or
foo?.run { subString(1) }
This is because the top-level function run accept anything Any & Any?. so an extension function with Null Receiver doesn't checked by Kotlin in runtime.
// v--- accept anything
public inline fun <T, R> T.run(block: T.() -> R): R = block()
Indeed, the inline function run is generated by Kotlin without any assertions if the receiver can be nullable, so it is more like a noinline function generated to Java code as below:
public static Object run(Object receiver, Function1<Object, Object> block){
//v--- the parameters checking is taken away if the reciever can be nullable
//Intrinsics.checkParameterIsNotNull(receiver, "receiver");
Intrinsics.checkParameterIsNotNull(block, "block");
// ^--- checking the `block` parameter since it can't be null
}
IF you want to call it in a safety way, you can use safe-call operator ?. instead, for example:
val foo: String? = null
// v--- short-circuited if the foo is null
foo?.run { println("foo") }