3 added 1 character in body
source | link
enum Day {
    FIRST("A partridge in a pear tree."),
    SECOND("Two turtle doves and"),
    THIRD("Three French hens,"),
    FOURTH("Four calling birds"birds,"),
    FIFTH("Five golden rings!"),
    SIXTH("Six geese a-laying,"),
    SEVENTH("Seven swans a-swimming,"),
    EIGHTH("Eight maids a-milking,"),
    NINETH("Nine ladies dancing,"),
    TENTH("Ten lords a-leaping,"),
    ELEVENTH("Eleven Pipers piping,"),
    TWELFTH("Twelve drummers drumming,");

    private final String line;

    private Day(String line) {
        this.line = line;
    }
}
enum Day {
    FIRST("A partridge in a pear tree."),
    SECOND("Two turtle doves and"),
    THIRD("Three French hens,"),
    FOURTH("Four calling birds"),
    FIFTH("Five golden rings!"),
    SIXTH("Six geese a-laying,"),
    SEVENTH("Seven swans a-swimming,"),
    EIGHTH("Eight maids a-milking,"),
    NINETH("Nine ladies dancing,"),
    TENTH("Ten lords a-leaping,"),
    ELEVENTH("Eleven Pipers piping,"),
    TWELFTH("Twelve drummers drumming,");

    private final String line;

    private Day(String line) {
        this.line = line;
    }
}
enum Day {
    FIRST("A partridge in a pear tree."),
    SECOND("Two turtle doves and"),
    THIRD("Three French hens,"),
    FOURTH("Four calling birds,"),
    FIFTH("Five golden rings!"),
    SIXTH("Six geese a-laying,"),
    SEVENTH("Seven swans a-swimming,"),
    EIGHTH("Eight maids a-milking,"),
    NINETH("Nine ladies dancing,"),
    TENTH("Ten lords a-leaping,"),
    ELEVENTH("Eleven Pipers piping,"),
    TWELFTH("Twelve drummers drumming,");

    private final String line;

    private Day(String line) {
        this.line = line;
    }
}
2 edited body
source | link
// Changed visibility modifier to private
private static String versesBelow(int verse) {
    return generate(verse, TwelveDaysOfChristmas::versesBelow, "", verse(verse), "");
}

private static String linesBelow(int line) {
    return generate(line, TwelveDaysOfChristmas::linesBelow, "", line(line), "");
}
// Changed visibility modifier to private
private static String versesBelow(int verse) {
    return generate(verse, TwelveDaysOfChristmas::versesBelow, verse(verse), "");
}

private static String linesBelow(int line) {
    return generate(line, TwelveDaysOfChristmas::linesBelow, "", line(line));
}
// Changed visibility modifier to private
private static String versesBelow(int verse) {
    return generate(verse, TwelveDaysOfChristmas::versesBelow, "", verse(verse));
}

private static String linesBelow(int line) {
    return generate(line, TwelveDaysOfChristmas::linesBelow, line(line), "");
}
1
source | link

I am required to write a program that prints the Twelve Days of Christmas without loops or conditionals...

Usually, that's code for using try-catch to purposefully let parts of your code fail (e.g. when determining an array index), and then remediate that in the catch. Makes for a 'fun' academic exercise such as this, just bear in mind this should strictly be only for fun, and not for writing clean, quality code. :)

Commenting and hard-coding

public static String song() {
    //The song consists of all verses up to verse 12, but the index starts from 0
    //Print verses 0 to 11
    return versesBelow(11);
}

This comment is redundant as it does not say anything more than what the code is plainly doing, but more importantly, 11 is hard-coded here. Since you already have an array, you can do something like array.length - 1 here so that it's easier for you to add extra lines in the future (e.g. 365 days of Christmas for non-leap-years).

Code duplication

Your versesBelow() and linesBelow() methods are very similar, so you may want to consider doing some de-duplication here by generalizing the problem...

// Function<T, R> is a Java 8 feature
private static generate(int x, Function<Integer, String> function, 
                            String before, String after) {
    String previous;
    try {
        int i = 1 / x;
        previous = function.apply(x- 1);
    }
    catch(ArithmeticException ex) {
        previous = "";
    }
    return before + previous + after;
}
  • We know we need to append the output from the previous day, except for the first.
  • Using a previous placeholder, try to perform our 'stop condition'. If we catch the expected Exception, it means we can concatenate with "".
  • We need before and after here as both methods require the previous output to be concatenated differently.

The two methods can then be refactored as such (using method references):

// Changed visibility modifier to private
private static String versesBelow(int verse) {
    return generate(verse, TwelveDaysOfChristmas::versesBelow, verse(verse), "");
}

private static String linesBelow(int line) {
    return generate(line, TwelveDaysOfChristmas::linesBelow, "", line(line));
}

Alternate modeling

When you have two enumerated String[] arrays where there's a close relationship between one and the other, you can consider representing them as an enum. This lets you reference one value with the other, almost like a key-value relationship. For example:

enum Day {
    FIRST("A partridge in a pear tree."),
    SECOND("Two turtle doves and"),
    THIRD("Three French hens,"),
    FOURTH("Four calling birds"),
    FIFTH("Five golden rings!"),
    SIXTH("Six geese a-laying,"),
    SEVENTH("Seven swans a-swimming,"),
    EIGHTH("Eight maids a-milking,"),
    NINETH("Nine ladies dancing,"),
    TENTH("Ten lords a-leaping,"),
    ELEVENTH("Eleven Pipers piping,"),
    TWELFTH("Twelve drummers drumming,");

    private final String line;

    private Day(String line) {
        this.line = line;
    }
}

Generating verses

With the enum in place, we now need to generate the verse for each Day. Since an enum is also a class, we can conveniently override its toString() for a more 'programmer-friendly' form.

enum Day {
    // ...

    private String getVerse() {
        String next;
        try {
            next = values()[ordinal() - 1].getVerse();
        } catch (ArrayIndexOutOfBoundsException e) {
            next = "";
        }
        return String.format("%s%n%s", line, next);
    }

    @Override
    public String toString() {
        return String.format("On the %s day of Christmas, my true love gave to me,%n%s",
                name().toLowerCase(), getVerse());
    }
}

Instead of catch-ing for an ArithmeticException, we can just handle for the case where ordinal() - 1 gives us -1, a special case only for the first day.

Look ma, no loops

What's wrong with just printing the lyrics literally then, with these restrictions? - myself

I meant that partly in-jest, but if we really need to avoid any loops, we can also call each enum value one-by-one to print them:

public static void main(String[] args) {
    System.out.println(Day.FIRST);
    System.out.println(Day.SECOND);
    System.out.println(Day.THIRD);
    System.out.println(Day.FOURTH);
    System.out.println(Day.FIFTH);
    System.out.println(Day.SIXTH);
    System.out.println(Day.SEVENTH);
    System.out.println(Day.EIGHTH);
    System.out.println(Day.NINETH);
    System.out.println(Day.TENTH);
    System.out.println(Day.ELEVENTH);
    System.out.println(Day.TWELFTH);
}