Java beginners: Writing classes that actually compile

Hi Community!

I’ve started the Java track. When I complete the exercises, I always make a second version of the solution that contains a main (with a println) that compiles so I can actually run and test it. I usually add a constructor and a main and adapt the rest of the code so it works.

The thing is, the test for the exercise then doesn’t pass anymore. But javac works fine, and the class prints out what I want. Now, it would be cool if we could submit a “free solution” that can be submitted as long as it compiles. It would be a cool way of sharing creative iterations on the basis of the exercises.

For example, for the Blackjack exercise, I have a Blackjack.class that runs smoothly. I created a “hand” object with three cards, and it prints out the recommendation. I’d love to share that and maybe even collect feedback on it.

Would a “free submission” feature make sense, or would it be redundant?

It seems to me like you are developing locally on your own machine.
You can already submit iterations that don’t pass the tests via the Exercism CLI. You can then request a code review. Maybe that is what you are looking for?

1 Like

Hi @fap,

Thank you for getting back to me! Your idea is great, I tried it out and it works like a charm.

Thanks again, and have a fantastic day!

Can you show an example of this, where the tests pass initially, and then you make the changes you want and now the tests do not pass?

Perhaps a version of TwoFer that is as per the tests and then as you have your version that “works” but does not pass the tests.

We might then put that logic in a test guard.

public class Main {
    public static void main(String[] args) {
        if (System.getProperty("org.junit.platform.launcher.TestExecutionListener.disabled") != null) {
            System.out.println("Program is being run in a test environment.");
        } else {
            System.out.println("Program is not being run in a test environment.");
        }
    }
}

Hi @kotp, sure! Thank you for taking the time.

I’ve seen the TwoFer exercise only over on the Groovy track, but the same is true there.
Here’s my solution of TwoFer that passed the Exercism test:

class TwoFer {

    static String twoFer(String name) {
        if (name) {
            return "One for " + name + ", one for me."
        } else {
            return "One for you, one for me."
        }
    }
}

Now, since I love to see things in action (especially to see if it still works when I tweak it), I always make a version that spits out something in the command line. Here’s the “actionable” version:

class TwoFer {
    
    def name;

    public TwoFer(String name) {
        this.name = name;
    }
    
    public String decider() {
        if (name) {
            return "One for " + name + ", one for me."
        } else {
            return "One for you, one for me."
        }
    }
    
    static main(args) {
    def walter = new TwoFer("Walter");
    def rando = new TwoFer(null);
    
    println(walter.decider());
    println(rando.decider());
    }
}

To be honest, the groovy code just gets a time-out error.
In Java, I get actual failed tests. Here, I don’t have two-fer, but I can give you Blackjack. Here’s the solution that passes the Exercism tests:

public class Blackjack {

    public int parseCard(String card) {
        switch (card) {
            case "ace":
                return 11;
            case "two":
                return 2;
            case "three":
                return 3;
            case "four":
                return 4;
            case "five":
                return 5;
            case "six":
                return 6;
            case "seven":
                return 7;
            case "eight":
                return 8;
            case "nine":
                return 9;
            case "ten": case "jack": case "queen": case "king":
                return 10;
            default:
                return 0;
        }
    }

    public boolean isBlackjack(String card1, String card2) {
        if (parseCard(card1) + parseCard(card2) == 21) {
            return true;} else {
            return false;
            }
        }
    
    public String largeHand(boolean isBlackjack, int dealerScore) {
        if (isBlackjack == true) {
            if (dealerScore < 10) {
                return "W";
            } else {
                return "S";
            }
        } else {
            return "P";
        }
    }
    
    public String smallHand(int handScore, int dealerScore) {
        if (handScore > 16) {
            return "S";
        } else if (handScore < 12) {
            return "H";
        } else if (dealerScore > 6) {
            return "H";
        } else {
            return "S";
        }
    }

    // FirstTurn returns the semi-optimal decision for the first turn, given the cards of the player and the dealer.
    // This function is already implemented and does not need to be edited. It pulls the other functions together in a
    // complete decision tree for the first turn.
    public String firstTurn(String card1, String card2, String dealerCard) {
        int handScore = parseCard(card1) + parseCard(card2);
        int dealerScore = parseCard(dealerCard);

        if (20 < handScore) {
            return largeHand(isBlackjack(card1, card2), dealerScore);
        } else {
            return smallHand(handScore, dealerScore);
        }
    }
}

And here’s the version that I can compile and run in the command line, but it fails the Exercism tests:

public class Blackjack {

    String card1;
    String card2;
    String dealerCard;

    public Blackjack(String a, String b, String c) {
        card1 = a;
        card2 = b;
        dealerCard = c;
    }

    public int parseCard(String card) {
        switch (card) {
            case "ace":
                return 11;
            case "two":
                return 2;
            case "three":
                return 3;
            case "four":
                return 4;
            case "five":
                return 5;
            case "six":
                return 6;
            case "seven":
                return 7;
            case "eight":
                return 8;
            case "nine":
                return 9;
            case "ten": case "jack": case "queen": case "king":
                return 10;
            default:
                return 0;
        }
    }

    public boolean isBlackjack(String card1, String card2) {
        if (parseCard(card1) + parseCard(card2) == 21) {
            return true;} else {
            return false;
            }
        }
    
    public String largeHand(boolean isBlackjack, int dealerScore) {
        if (isBlackjack == true) {
            if (dealerScore < 10) {
                return "W";
            } else {
                return "S";
            }
        } else {
            return "P";
        }
    }
    
    public String smallHand(int handScore, int dealerScore) {
        if (handScore > 16) {
            return "S";
        } else if (handScore < 12) {
            return "H";
        } else if (dealerScore > 6) {
            return "H";
        } else {
            return "S";
        }
    }

    // FirstTurn returns the semi-optimal decision for the first turn, given the cards of the player and the dealer.
    // This function is already implemented and does not need to be edited. It pulls the other functions together in a
    // complete decision tree for the first turn.
    public String firstTurn() {
        int handScore = parseCard(card1) + parseCard(card2);
        int dealerScore = parseCard(dealerCard);

        if (20 < handScore) {
            return largeHand(isBlackjack(card1, card2), dealerScore);
        } else {
            return smallHand(handScore, dealerScore);
        }
    }

    public static void main(String[] args){
        Blackjack hand = new Blackjack("two", "queen", "seven");

        System.out.println(hand.firstTurn());
    }
}

I would expect that the library should still pass on Exercism, since the tests for most (unconfirmed, but expected) tracks should be “library tests” rather than “application tests”.

So we should be able to guard against something (such as “Is this file being ran directly, or loaded as a library? If so, then execute this “running code” that uses the library”) so that you can have both.

In Ruby, I would write the library code, and then the following guard, and submit it with the exercise file, so that those reading the iteration can seen a example of how to use that library:

class TwoFer

  # The things that provide the work product from the library

end

if __FILE__ == $PROGRAM_NAME

  # use the library like this

  puts TwoFer.two_fer

end

That sounds great! :star_struck:

Today, I uploaded my solution to java’s BirdWatcher exercise both without and with a main method. And both passed the tests! :tada:

The link goes to the exercise link, but not your solution.

It can be better to post your code as it is in an iteration, here. Only a select few can probably see what you intend them to see with the link given as it is. (You would be that select few! ;) ).

Oops, my bad :sweat_smile: still learning my way around here. Here’s the code that passed both tests (i.e. it made both the local gradle test and javac happy, and passed the online test):

class BirdWatcher {
    private final int[] birdsPerDay;
    int[] lastWeek = {0, 2, 5, 3, 7, 8, 4};

    public BirdWatcher(int[] birdsPerDay) {
        this.birdsPerDay = birdsPerDay.clone();
    }
    public int[] getLastWeek() {
        return lastWeek;
    }
    public int getToday() {
        return birdsPerDay[6];
    }
    public void incrementTodaysCount() {
        birdsPerDay[6] += 1;
    }
    public boolean hasDayWithoutBirds() {
        int i = 0;
        for(int c : birdsPerDay) {
            if(c == 0) {
                i += 1;
            } else {
                break;
            }
        }
        if(i > 0) {
            return true;
        } else {
            return false;
        }
    }
    public int getCountForFirstDays(int numberOfDays) {
        int b = 0;
        if(numberOfDays <= birdsPerDay.length) {
            for (int i = 0; i < numberOfDays; i++) {
                b += birdsPerDay[i];        
            }
            return b;
            } else {
            System.out.println("\nWe only have the last " + birdsPerDay.length + " days on record. Returning results for the last " + birdsPerDay.length + " days.");
            for (int i = 0; i < birdsPerDay.length; i++) {
                b += birdsPerDay[i];        
            }
            return b;
            }
        }
    public int getBusyDays() {
        int n = 0;
        for (int i : birdsPerDay) {
            if (i > 4) {
                n += 1;
            } else {
                n += 0;
            }            
        }
        return n;
    }

    public static void main(String[] args) {
        int[] watchCount = { 11, 3, 0, 12, 8, 2, 1, 299};
        BirdWatcher watch = new BirdWatcher(watchCount);
        System.out.println("\nThe bird count for the 3 first days of the current counting period is " + watch.getCountForFirstDays(3) + ".\n\n(Expected: 14)\n");
    }
}

Just out of curiosity, I’ll try and link to my public solution one more time :nerd_face::

udomai’s solution for BirdWatcher

@udomai Correct me if i’m wrong, but your local versions have the signatures of the methods changed. The blackjack solution that passes the online tests accepts a String firstTurn(String card1, String card2, String dealerCard) but you are submitting a String firstTurn(). Eh sorry, but it’s not possible to pass the online tests with incorrect functions.

Also, if you add your own constructor that accepts >0 arguments, make sure that you explicitly define the default constructor that accepts 0 arguments.

Having a main defined or not should make 0 difference. A main is required if we are trying to run an application. We don’t need it for class compilation.

Hi @tasx, thanks for your insights! I’m reading up on default constructors. How does Java do this, doesn’t it create a default constructor with 0 arguments when the class is compiled? I’m curious about good practice and the motivations behind it.

About the tests: My post is more about being able to publish and share solutions that don’t pass the Exercism tests (for the reasons you pointed out above), provided javac is happy with them. But that would probably mean (a) a lot more load on the Exercism servers, and (b) that Exercism has basically no control over what gets published.
The more I get familiar with the framework, the more I see why my proposal might not be easy to put in place.

Any which way, thank you all for taking the time! Back to coding :nerd_face:

It will automatically create a constructor that takes no arguments only if there are no constructors defined. If you define a constructor that takes >0 arguments, what you define is what you get. In this case the online tests will probably fail if because they are relying on the no-argument constructor. So, you do need to explicitly define one even if it’s empty.