Java as your Kotlin Study Buddy

Java as your Kotlin Study Buddy

Switching to a new primary language can be daunting. We know the ins and outs of the language we have been working with for years, and all the sudden we’re newbies again. While this is always a good exercise to put yourself in, and comes with lots of learning, there is also a bit of vulnerability. If your primary language has been Java, and you’re making the switch to Kotlin, you might be feeling a bit of this insecurity.

You’re in luck in this case. The knowledge you have of Java is in no way obsolete.

Kotlin is a language that runs on the Java Virtual Machine, with the same bytecode that is at the core of the Java we know. So there is a question you should ask:

How can we take advantage of this?

Because Kotlin compiles down to bytecode, we can decompile that bytecode into the equivalent Java. My favorite tool to do this is the Kotlin Bytecode Viewer right in Android Studio and IntelliJ IDEA. I like always having it right there in my IDE.

Other options include the ClassyShark Bytecode viewer, or Luyten. By using one of these latter options, you are able to view the decompiled Java from class files where you may not have the source. You can also compare results from different decompilers.

How can this be useful to you? Let’s say you come across this snippet of code:

val (phoneNumber, address) = friend.contactInfo

You know you have a friend object, and Friend has a contactInfo property. That property is a ContactInfo object that has a phone number and an address.

data class ContactInfo(val phoneNumber: String, val address: String)

What’s weird here is that we’re assigning the value of contactInfo to val (phoneNumber,eNumber, address).

This is not the Java I know. We can decompile the bytecode for this Kotlin to get a better sense of what exactly is going on.

ContactInfo var3 = friend.getContactInfo();
String phoneNumber = var3.component1();
String address = var3.component2();
var3 = null;

This Java clears a bit of this up.

What this is doing is getting the contact information from the friend object, then getting the phoneNumber and address from there. Not bad.

We can find out more about these numbered component methods by decompiling the ContactInfo data class.

public final class ContactInfo {
  @NotNull
  private final String phoneNumber;
  @NotNull
  private final String address;

  @NotNull
  public final String getPhoneNumber() {
     return this.phoneNumber;
  }

  @NotNull
  public final String getAddress() {
     return this.address;
  }

  public ContactInfo(@NotNull String phoneNumber, @NotNull String address) {
     Intrinsics.checkParameterIsNotNull(phoneNumber, "phoneNumber");
     Intrinsics.checkParameterIsNotNull(address, "address");
     super();
     this.phoneNumber = phoneNumber;
     this.address = address;
  }

  @NotNull
  public final String component1() {
     return this.phoneNumber;
  }

  @NotNull
  public final String component2() {
     return this.address;
  }

  @NotNull
  public final ContactInfo copy(@NotNull String phoneNumber, @NotNull String address) {
     Intrinsics.checkParameterIsNotNull(phoneNumber, "phoneNumber");
     Intrinsics.checkParameterIsNotNull(address, "address");
     return new ContactInfo(phoneNumber, address);
  }

  // $FF: synthetic method
  // $FF: bridge method
  @NotNull
  public static ContactInfo copy$default(ContactInfo var0, String var1, String var2, int var3, Object var4) {
     if((var3 & 1) != 0) {
        var1 = var0.phoneNumber;
     }

     if((var3 & 2) != 0) {
        var2 = var0.address;
     }

     return var0.copy(var1, var2);
  }

  public String toString() {
     return "ContactInfo(phoneNumber=" + this.phoneNumber + ", address=" + this.address + ")";
  }

  public int hashCode() {
     return (this.phoneNumber != null?this.phoneNumber.hashCode():0) * 31 + (this.address != null?this.address.hashCode():0);
  }

  public boolean equals(Object var1) {
     if(this != var1) {
        if(var1 instanceof ContactInfo) {
           ContactInfo var2 = (ContactInfo)var1;
           if(Intrinsics.areEqual(this.phoneNumber, var2.phoneNumber) && Intrinsics.areEqual(this.address, var2.address)) {
              return true;
           }
        }

        return false;
     } else {
        return true;
     }
  }
}

Looking specifically at those ordered component methods, we see that they return the phoneNumber, and address fields, in order.

@NotNull
public final String component1() {
  return this.phoneNumber;
}

@NotNull
public final String component2() {
  return this.address;
}

When we have a data class, we automatically get these ordered components for destructuring class declarations. This is what we were seeing in that first snippet of code we decided to evaluate. Because data classes are so simple to create, this feature can be used in many places. You can use it to return multiple related values from a function, and to iterate through a map.

for ((phoneNumber, address) in addressBook) {
 // Do something with phoneNumber and address
}

Summary

Learning a new language is fun, and we can make it less of a hurdle by applying the knowledge we already do have. This was one example of how we can use our knowledge of Java to get to know some unfamiliar Kotlin code. Try it yourself next time you run into something you wish you  knew more about!

Victoria was drawn to programming because of its potential to help people in a meaningful way. She is currently a Software Developer at Collective Idea, building mobile and web applications. The conferences she has spoken at have been an enjoyable way to connect with the technical community, and exchange information with others. In her spare time, Victoria enjoys dancing, and curling up with a good book.

You can follow her on Twitter or visit her website website at victoriagonda.com

0 replies

Leave a Reply

Want to join the discussion?
Feel free to contribute!

Leave a Reply

Your email address will not be published. Required fields are marked *