Friends of OpenJDK Today

New Book: “Java Challengers”

May 03, 2021

Author(s)

  • Rafael del Nero

    Java Champion, a passionate and creative developer who possesses strong technical knowledge and delivers solid systems focused on Spring and Java EE specifications for cloud applications. More than 10 years' ... Learn more

To get the best jobs and create massive value, you need to know Java very well. The newly released "Java Challengers" book is a way for you to challenge yourself with fun code challenges so that you will become a better Java developer.

This book contains more than 70 well-elaborated Java Challenges that will help you break your limits on your Java skills. Want to challenge yourself and become better? The Java Challengers is the book for you!

For each Java Challenge you get a full explanation to fully prepare you to beat the Java Challenge!

To tease you with the Java Challengers book, try out the following challenges and see if you can solve them. We've given a few clues to think about, should you need them, though not the answers themselves! Try and see if you can figure out the below, before running the code to see the answers. 🙂

Jedi Covariant Polymorphism Challenge

In this challenger, we have an abstract class and another class that extends it. The concept of covariant types is being used here. What do you think the output will be after the main method is compiled and run? Will the code compile?

public class YodaForce {
      public static void main(String... covariantType) {
          System.out.println(new Yoda().useSaber()); 
          System.out.println(new Yoda().attack()); 
          System.out.println(new Yoda().lightForce); // Line 5
      }
      static abstract class LightForce { 
          int lightForce;
          abstract Object useSaber(); 
          abstract long attack();

          LightForce() {
            lightForce++;
          }
      }
      static class Yoda extends LightForce { 
          String useSaber() {
            return "useSaber"; 
          }
          long attack() { 
            return 99999;
          } 
      }
}

A.
useSaber
99999
3

B.
Compilation error at line 6 useSaber

C.
useSaber
99999
1

D.
RuntimeException

Will this code compile and run fine? The concepts demonstrated here are mostly about polymorphism. In the first method invocation, we are simply invoking the overridden useSaber method:

System.out.println(new Yoda().useSaber());

Note that we are using a covariant return type for the overridden method: the Yoda subclass’s useSaber method returns a String instead of an Object. This method prints “useSaber”.

In the second method invocation we are invoking another overridden method, attack:

System.out.println(new Yoda().attack());

We can’t use a covariant type here because there is no inheritance between primitive types; both methods return a long. The output of this method call is 99999.

Finally, we print the lightForce variable that is incremented each time the LightForce class is instantiated:

System.out.println(new Yoda().lightForce); // Line 5

Even though we instantiate the LightForce class three times, note that the lightForce variable is an instance variable. Therefore, the variable will be reset every time a new instance is created. We have three instantiations, but the result of this variable will be 1 for each of the three instances.

In conclusion, the correct answer is ...

Mysterious Door Lambda Challenge

By running the following code, can you guess what will happen?

import java.util.Arrays;
import java.util.List;

public class MysteriousDoorLambdaChallenge {

    public static void main(String... theDoors) {
        int doorNumber = 0;
        doorNumber++;
        List<String> doors = Arrays.asList("A", "B", "C");
        doors.forEach(e -> {
            System.out.println(e + doorNumber); // # Line 11
        });
    }
    
}

A.
A0
B1
C2

B.
A1
B2
C3

C.
Compilation Error at line 11

D.
Unpredictable

Lambdas can only access variables that are final or effectively final. As mentioned above in its essence should access only immutable data from outside.

Note that the doorNumber variable is being changed when we increment it. Therefore, the doorNumber variable is not effectively final anymore from that moment. Simply put, effectively final variables are variables that when given a value, this value will be never changed.

In conclusion, the correct answer is ...

KeyMaker Overloading Challenge

The following challenger explores the concept of how primitive types are interpreted by the JVM when argument values are hardcoded. Can you figure out what will happen when this code is executed?

public class KeyMakerOverloading {

    public static void main(String... primitiveOverloading) {
        makeKey(1);
        makeKey(1F);
        makeKey('1');
        makeKey(1.0);
    }

    static void makeKey(short shortCode) {
        System.out.println("short:" + shortCode);
    }

    static void makeKey(long longCode) {
        System.out.println("long:" + longCode);
    }

    static void makeKey(float floatCode) {
        System.out.println("float:" + floatCode);
    }
    static void makeKey(double floatCode) {
        System.out.println("double:" + floatCode);
    }
}

A.
short:1
float:1.0
short:49
double:1.0

B.
long:1
float:1.0
short:1
float:1.0

C.
short:1
float:1.0
long:49
float:1.0

D.
long:1
float:1.0
long:49
double:1.0

Let’s analyze the code. In the first invocation, we pass the hardcoded value 1:

makeKey(1);

int is the default type interpreted to non-decimal numbers that fit within this type’s range, so the JVM will convert this hardcoded number to int. But none of the overloaded methods takes an int, so the type will be promoted to a wider one. The first method the JVM finds that can accommodate this type is this one:

static void makeKey(long longCode) { … }

Therefore, the first output will be:

long:1

In the second method invocation, we are passing an explicit float because we are using the letter F after the number:

makeKey(1F);

As we already have a corresponding method for the float type, the output for this invocation will be:

float:1.0

In the third method invocation, we pass a char:

makeKey('1');

There’s no method that takes a char, so type promotion will again be used here. As the diagram in figure 5.1 shows, a char can be promoted to an int, which as we’ve already seen will be promoted further to a long in this example. The char value will be converted to the corresponding number in the ASCII table, which is 49. Therefore, the output will be:

long: 49

Finally, the number 1.0 will be automatically converted by the JVM to the type double, and then it will invoke the method that receives a double:

double:1.0

Therefore, the correct answer will be...

If you want more it’s time to get the Java Challengers book for you and beat all the challenges! Are you ready for them? Go here for more!

Author(s)

  • Rafael del Nero

    Java Champion, a passionate and creative developer who possesses strong technical knowledge and delivers solid systems focused on Spring and Java EE specifications for cloud applications. More than 10 years' ... Learn more

Comments (0)

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

Highlight your code snippets using [code lang="language name"] shortcode. Just insert your code between opening and closing tag: [code lang="java"] code [/code]. Or specify another language.

Save my name, email, and website in this browser for the next time I comment.

Subscribe to foojay updates:

https://foojay.io/feed/
Copied to the clipboard