Showing posts with label autoboxing. Show all posts
Showing posts with label autoboxing. Show all posts

Thursday, 28 October 2021

Kotlin, AssertJ and Overloads

So, I ran into a novell problem recently.

I've got the following Java code, which is a basic POJO following the Java Bean conventions:

  private Integer orderNumber;

  private Integer amount;

  public Integer getOrderNumber() 
  {
    return orderNumber;
  }

  public void setOrderNumber(Integer orderNumber) 
  {
    this.orderNumber = orderNumber;
  }

  public Integer getAmount() 
  {
    return amount;
  }

  public void setAmount(Integer amount) 
  {
    this.amount = amount;
  }

Now, I've got a Kotlin1 class that I use for testing, and I am using AssertJ2 as the library for comparing values.

At first try it looked very easy:

assertThat(get.orderNumber).isNull()
assertThat(get.amount).isNull()

This caused a serious java.lang.NullPointerException: get.orderNumber must not be null.

This is a typical Kotlin message, as Kotlin has Null safety built in. Kotlin is telling me that orderNumber may not be null. In the Java code it all seems fine, it's an Integer and nulls are allowed.

So what went wrong here?

Well, if I check out which assertThat is being used, I end up at:

  public static AbstractIntegerAssert<?> assertThat(int actual) {
    return new IntegerAssert(actual);
  }

And so we see now that it'll automatically be downgraded to a java int. Which Kotlin intercepts as being bad-form when using nulls.

So how to fix this?

I came up with the following little ugly hack:

assertThat(get.orderNumber as Any?).isNull()
assertThat(get.amount as Any?).isNull()

Now, all of a sudden, the method called is:

  public static <T> ObjectAssert<T> assertThat(T actual) {
    return new ObjectAssert(actual);
  }

Which is what I want.

Unfortunately this is neither obvious nor clear.

It's the price we pay for many many overloaded methods. Sometimes the compiler will just pick the wrong one.

However, to be fair, in most cases the Compiler picks the right one. This is just one of those cases, where we have to be specific.

There's some references to tickets on Kotlin in the References.

Addendum

The proper way to fix this problem, is of course, to add an @Nullable to the method in de Java class (if you can). This causes Kotlin to automatically assume it can be "null", and it therefore does not need casting.

P.S. I'm trying out a new code formatter3. It's nice in so far that it does not require additional installs, but it does add spans and styling directly. Pick your poison, I guess.

References

[1] Kotlin Language
https://kotlinlang.org/
[2] AssertJ
https://assertj.github.io/doc/
[3] Source code beautifier / syntax highlighter – convert code snippets to HTML « hilite.me
http://hilite.me/
YouTrack IntelliJ - Argument of Java platform boxed type resolves to primitive parameter method signature from Kotlin
https://youtrack.jetbrains.com/issue/KT-21285
GitHub AssertJ - isNull assertion broken in Kotlin when calling a Java class #1115
https://github.com/assertj/assertj-core/issues/1115

Thursday, 10 December 2015

true vs. Boolean.TRUE

I saw the following code below at my work, and it got me thoroughly confused. I found the statement above to be unnecessarily complicated.

private boolean useAsReferenceModel = Boolean.FALSE;

Boolean.FALSE returns a Boolean, which is a wrapper2 around the boolean primitive types.

So, in the example above, the Boolean wrapper is auto-unboxed3 to the primitive type false.

So, I changed the code to the simple and straightforward:
private boolean useAsReferenceModel = false;
Let us not make the world more complicated than it really is.

References

[1] StackOverflow - Java what is the difference between false and Boolean.FALSE
http://stackoverflow.com/questions/20090306/java-what-is-the-difference-between-false-and-boolean-false
[2] Wikipedia - Primitive Wrapper Classes
https://en.wikipedia.org/wiki/Primitive_wrapper_class
[3] Oracle JavaDoc - Autoboxing and unboxing
https://docs.oracle.com/javase/tutorial/java/data/autoboxing.html

Monday, 22 December 2014

The Terrible Dangers of Autoboxing (Part 3) - Solution

The solution to my previous post regarding the Terrible Dangers of Autoboxing, is written below.

In our case, the type of the second expression is 0L (long) and the third is null but returns from a Long method.

Java assumes that the second and third operands have types that are convertible to numeric types.

In that case, the conditional operator causes binary numeric promotion.2

During binary numeric promotion, it is possible an unboxing is performed3 (on null in our case), which consequently fails with a NullPointerException.

The solution is to replace
return hasSurfaceArea() && getSurfaceInM2() == null ? 0L : getSurfaceInM2();
With:
return hasSurfaceArea() && getSurfaceInM2() == null ? Long.valueOf(0) : getSurfaceInM2();

P.S. Rewriting the conditional expression into an if statement, will also solve it.

Quote

Here is a brilliant quote that every Java programmer needs to remember and understand (especially that last bit):
“Autoboxing and unboxing blur the distinction between primitive types and reference types, but they do not eliminate it.1”

References

[1] Java SE Documentation - Autoboxing
http://docs.oracle.com/javase/7/docs/technotes/guides/language/autoboxing.html
[2] Chapter 15.25 Conditional Operator ? :
Java Language Specification
[3] Chapter 5.6.2 Binary Numeric Promotion
Java Language Specification
Java Tutorials - Autoboxing and Unboxing
http://docs.oracle.com/javase/tutorial/java/data/autoboxing.html

Monday, 15 December 2014

The Terrible Dangers of Autoboxing (Part 3)

Part 2 can be found here.


Please explain why the code throws a NullPointerException.

Output:
/opt/tools/java/jdk1.7.0_65/bin/java com.mrbear.autoboxing.Plot
0
3
3
Exception in thread "main" java.lang.NullPointerException
    at com.mrbear.autoboxing.Plot.getSurfaceArea(Plot.java:28)
    at com.mrbear.autoboxing.Plot.main(Plot.java:39)


Process finished with exit code 1
Also, please mention how to fix it.

((Solution))

Tuesday, 27 July 2010

The Terrible Dangers of Autoboxing (Part 2)

Solution to part 1:
j is equal to m
j is equal to K
K is equal to m

The result of one of the Object being null, will of course cause a NullPointerException.

Part 2:

class Autoboxing
{ 

    public static void main(String[] args)
    {
        Integer a = 10;
        Integer b = 10;
        Integer c = 1000;
        Integer d = 1000;
        System.out.println(a == b); //true
        System.out.println(c == d); //false
    }

};

Please explain.

(Found it in my Linkedin groups "Sun Certified Java Programmer" here)


References

Most common gotchas in Java
http://vanillajava.blogspot.nl/2014/02/most-common-gotchas-in-java.html

Tuesday, 2 March 2010

The Terrible Dangers of Autoboxing (Part 1)

Below piece of code indicates why some manner of caution is called for when using ints and Integers interchangeably.

Can you tell what the code will do?

Can you tell what happens when Integer K = null?

public class Autoboxing
{

    public static void main(String [] testAutobox)
    {
        int j = 5;
        Integer K = new Integer(5);
        Integer L = new Integer(5);
        int m = 5;
        if (j == m)
        {
            System.out.println("j is equal to m");
        }
        if (j == K)
        {
            System.out.println("j is equal to K");
        }
        if (K == L)
        {
            System.out.println("K is equal to M");
        }
        if (K == m)
        {
            System.out.println("K is equal to m");
        }
    }

}
Addendum: Recently I got a weird problem when I was debugging in Eclipse. Apparently autoboxing doesn't work very well when trying to use expressions to evaluate in the Debugger. Perhaps Autoboxing, in this case, doesn't work, and the two objects are compared based on their memory address as is the default behaviour.

References

Most common gotchas in Java
http://vanillajava.blogspot.nl/2014/02/most-common-gotchas-in-java.html