Showing posts with label varargs. Show all posts
Showing posts with label varargs. Show all posts

Friday, 12 July 2024

Kotlin: The Spead Operator

Recently ran into a brick wall trying to pass a varargs parameter to another function that also has a varargs parameter.

A colleague mentioned the "spread" operator to me and it took me a little while to find information about it.

An example

package org.mrbear.kotlin

enum class ErrorCode(val description: String) {
    OBJECT_NOT_FOUND("Object %s not found."), NO_DEFAULT_PROVIDED("No default provided for parameter %s."), MALFORMED_URL(
        "Malformed url (%s)"
    )
}

abstract class MyException : Exception {
    constructor(errorCode: ErrorCode, cause: Throwable, vararg params: Any) : super(
        String.format(
            errorCode.description,
            *params
        ), cause
    )

    constructor(errorCode: ErrorCode, vararg params: Any) : super(String.format(errorCode.description, *params))
}

class ObjectNotFoundException(vararg params: Any) : MyException(ErrorCode.OBJECT_NOT_FOUND, *params)

Now to throw it in a test.

class ExceptionTest {

    @Test(expectedExceptions = [ObjectNotFoundException::class], expectedExceptionsMessageRegExp = "Object User mrbear not found.")
    fun testException() {
        throw ObjectNotFoundException("User mrbear")
    }
}

References

Kotlin - Variable number of arguments (varargs)
https://kotlinlang.org/docs/functions.html#variable-number-of-arguments-varargs
Kotlin - Java varargs
https://kotlinlang.org/docs/java-interop.html#java-varargs
Baeldung - Convert Kotlin Array to Varargs
https://www.baeldung.com/kotlin/array-to-varargs
Baeldung - Varargs in Java
https://www.baeldung.com/java-varargs

Friday, 22 July 2022

Falling into a trap: decompiling java code

So my colleague was decompiling some Java code. We're using IntelliJ and then it happens automatically if you happen to select a Class file or something referencing a class file for which there are no sources attached.

And for the life of us, we couldn't understand why that code seemed to work.

Here's the decompiled code snippet.

protected Block createItem(String title, ItemProperties properties) {
  return this.createItem(title, properties);
}

It looked like it was going in circles, as the method was simply calling itself. This is bound to create a StackOverflowError when enough recursion happens.

However, no such problem occurred.

When looking at the source, however, we found it was not similar to the decompiled code:

protected Block createItem(String title, ItemProperties properties) {
  return createItem(title, properties, new SubItem[0]);
}

What turned out to be the issue, an empty array as the varargs at the end of a method is simply removed in the decompiled class version.

Hence our confusion.

It took us several minutes to find this out.

References

Baeldung - Varargs
https://www.baeldung.com/java-varargs

Wednesday, 12 September 2018

Pop quiz answer: varargs

The answer for the VarArgs Pop Quiz.

Testcase 1 : [Ljava.lang.String;@579bb367 is not null and has length 0
Testcase 2 : args is null
Testcase 3 : [Ljava.lang.String;@1de0aca6 is not null and has length 2
     first element is null

Process finished with exit code 0

The reason for the quiz is that there's a potential NullPointerException in there, that you need to be aware of.

According to reference [1], if the argument provided is compatible with String[], it is assumed that that is what you meant. And null is always compatible with any T[].

Also interesting enough and just to drive the point home, it means that the method signatures below are identical and Java will give you a big fat warning that you defined the same method twice.

public void vars(String... args);

public void vars(String[] args);

References

[1] Oracle Java Language Spec - 15.12.4.2. Evaluate Arguments
https://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.12.4.2
Blogpost - Pop Quiz: Varargs
https://randomthoughtsonjavaprogramming.blogspot.com/2018/09/pop-quiz-varargs.html

Friday, 7 September 2018

Pop quiz : varargs

Pop quiz: what is the output of the following program:

The answer in the following blog post.

Thursday, 20 October 2016

Potential heap pollution via varargs parameter

Reifiable versus Non-Reifiable

Type erasure is an important "feature" of Generics in Java. It means generics are not available at runtime, as they are effectively removed during compiling.

The reference in [1] has a much better explanation.

To quote:
“A reifiable type is a type whose type information is fully available at runtime.”
“Non-reifiable types are types where information has been removed at compile-time by type erasure.”

Generics versus Arrays

In Java, generics are non-reifiable and arrays are reifiable.

Problems can occur when we combine these two together.

Combining these two together can happen when using the varargs construction in Java.

The reason for this is that the varargs way of using method parameters is translated within the method as a array.

This can cause Heap pollution2 when combined with Generics.

As the compiler doesn't know when this happens (it depends on how the method deals with it), it throws out the warning.

Hence the need for the @SafeVarargs3 annotation for those methods where the software designers are certain the problem does not occur.

References

[1] Non-Reifiable Types
http://docs.oracle.com/javase/tutorial/java/generics/nonReifiableVarargsType.html
[2] 9.6.3.7. @SafeVarargs
http://docs.oracle.com/javase/specs/jls/se7/html/jls-9.html#jls-9.6.3.7
[3] Oracle JavaDoc - SafeVarargs
http://docs.oracle.com/javase/7/docs/api/java/lang/SafeVarargs.html
StackOverflow - Potential heap pollution via varargs parameter
http://stackoverflow.com/questions/12462079/potential-heap-pollution-via-varargs-parameter