Java Quine, Sans Semicolons
Well, if you were looking for a Java program that would print out
itself without using any semi-colons, you've found it. But why would
someone want to write a program that prints itself out? And why would
someone want to program in Java without using semi-colons? Both
questions remain mysteries. However, the solution isn't...
The Champion
From Josh Donnoe, we have the shortest, sans-semicolon Java quine in existence:
class Q{public static void main(String[]a){if(System.out.printf((a=new String[]
{"class Q{public static void main(String[]a){if(System.out.printf((a=new String[]{%c%s%1$c})[0],34,a[0])!=null){}}}"})[0],34,a[0])!=null){}}}
Note: You'll need to compile this as a single line.
Previous Winner: Java 5 with Generics!
From Daniel Pitts we have a wonderful demonstration of generics in Java:
class T{public static<X>void main(String[]a){if((a=new String[]{"class T{public static<X>void main(String[]a){if((a=new
String[]{%c%s%1$c})==(X)System.out.printf(a[0],34,a[0])){}}}"})==(X)System.out.printf(a[0],34,a[0])){}}}
I really like how generics are used here. The whole point of
X is to cast to Object without using as many
characters. And the point of casting to Object is to let
the == operation type-check.
Note: You'll need to compile this as a single line.
Any further ideas for making short Quines with special restrictions (like not having semicolons) are appreciated.
Short Versions in Java 5
Here are other versions I have here (my own creations). In some ways, this one is the easiest to understand:
public class SemilessQuine2Point0 {
public static void main(String[] src) {
if ((src = new String[] {
"public class SemilessQuine2Point0 {",
" public static void main(String[] src) {",
" if ((src = new String[] {",
"",
" }) == null){}",
" for (String line: src) {",
" if (line.length() == 0) {",
" for (String toQuote: src)",
" if (System.out.printf((char)34 + toQuote + (char)34",
" + (char)44 + (char)10) == null) {}",
" }",
" else",
" if (System.out.printf(line + (char)10) == null) {}}}}",
}) == null){}
for (String line: src) {
if (line.length() == 0) {
for (String toQuote: src)
if (System.out.printf((char)34 + toQuote + (char)34
+ (char)44 + (char)10) == null) {}
}
else
if (System.out.printf(line + (char)10) == null) {}}}}
This one was the previous champ, at 258 characters, until Daniel's version blew it out of the water:
public class R{R(String s){if(System.out.printf(s,34,s)==null){}}public static void main(String[]a){if(new R(
"public class R{R(String s){if(System.out.printf(s,34,s)==null){}}public static void main(String[]a){if(new R(%n%c%s%1$c)==null){}}}%n")==null){}}}
You can make it shorter by removing the newlines, but that change itself isn't very interesting.
Update 1: By using printf's 1$ notation,
I was able to trim this by 16 characters.
Update 2: Using the constructor trick creates the binding for
"s" with slightly
fewer characters than a previous version that used a
for-each loop.
A Longer Version (Java 5)
The short version is cute, but it uses the raw ASCII codes to avoid quoting hell. This version lives in quoting hell. Its technique is general enough to easily allow for any arbitrary string literal to appear in the program.
Compare this one to the Java 1.4 version below. Because printf is part
of Java 5, all of that reflection isn't necessary.
Longest, Craziest Version (Java 1.4)
Now, finally the interesting version! This one even reinvents the looping constructs from the ground up.
public class Quine {
public static void main(String[] args) {
if (new GetProgramString(new StringReceiver() {
public void receive(String theProgram) {
if (new Print(theProgram + "\n\n") == null) {}
if (new ConvertStringToStringReturningClass(theProgram, new StringReceiver() {
public void receive(String classThatReturnsString) {
if (new Print(classThatReturnsString + "\n") == null) {}
}}, new StringBuffer()) == null) {}
}}) == null) {}
}
}
class Print {
Print(String str) {
if (new Invoke(System.out, "print", new Class[] {String.class}, new Object[] {str}) == null) {}
}
}
class ForEachChar {
ForEachChar(String str, Do doBlock) {
if (new Body(str, doBlock, 0) == null) {}
}
static class Body {
Body(String str, Do doBlock, int i) {
while (i < str.length()) {
if (new Invoke(doBlock, "run", new Class[] {char.class},
new Object[] {new Character(str.charAt(i))}) == null) {}
if ((++i) == 0) {}
}
}
}
}
class Do {
public void run(char c) {}
}
class Invoke {
Invoke (Object thiz, String methodName, Class[] paramTypes, Object[] args) {
try {
if (thiz.getClass().getMethod(methodName, paramTypes).invoke(thiz, args) == null) {}
}
catch (Exception e) {
}
}
}
class ConvertStringToStringReturningClass {
ConvertStringToStringReturningClass(String str, StringReceiver sr, final StringBuffer buff) {
if (buff.append(
"//Call this method to get the string that represents (most of) the program, except for this\n" +
"//class itself.\n" +
"class GetProgramString {\n" +
" GetProgramString(StringReceiver sr) {\n" +
" if (new Invoke(sr, \"receive\", new Class[] {String.class}, new Object[] {\n" +
"\"") == null) {}
if (new ForEachChar(str, new Do() {
public void run(char c) {
if (c == '\"' || c == '\'' || c == '\\') {
if (buff.append("\\") == null) {}
if (buff.append(c) == null) {}
}
else if (c == '\n') {
if (buff.append("\\n\" + \n\"") == null) {}
}
else {
if (buff.append(c) == null) {}
}
}}) == null) {}
if (buff.append(
"\"\n" +
" }) == null) {}\n" +
" }\n" +
"}\n") == null) {}
if (new Invoke(sr, "receive", new Class[] {String.class}, new Object[] {buff.toString()}) == null) {}
}
}
class StringReceiver {
public void receive(String str) {}
}
//Call this method to get the string that represents (most of) the program, except for this
//class itself.
class GetProgramString {
GetProgramString(StringReceiver sr) {
if (new Invoke(sr, "receive", new Class[] {String.class}, new Object[] {
"public class Quine {\n" +
" public static void main(String[] args) {\n" +
" if (new GetProgramString(new StringReceiver() {\n" +
" public void receive(String theProgram) {\n" +
" if (new Print(theProgram + \"\\n\\n\") == null) {}\n" +
" if (new ConvertStringToStringReturningClass(theProgram, new StringReceiver() {\n" +
" public void receive(String classThatReturnsString) {\n" +
" if (new Print(classThatReturnsString + \"\\n\") == null) {}\n" +
" }}, new StringBuffer()) == null) {}\n" +
" }}) == null) {}\n" +
" }\n" +
"}\n" +
"\n" +
"class Print {\n" +
" Print(String str) {\n" +
" if (new Invoke(System.out, \"print\", new Class[] {String.class}, new Object[] {str}) == null) {}\n" +
" }\n" +
"}\n" +
"\n" +
"class ForEachChar {\n" +
" ForEachChar(String str, Do doBlock) {\n" +
" if (new Body(str, doBlock, 0) == null) {}\n" +
" }\n" +
" \n" +
" static class Body {\n" +
" Body(String str, Do doBlock, int i) {\n" +
" while (i < str.length()) {\n" +
" if (new Invoke(doBlock, \"run\", new Class[] {char.class},\n" +
" new Object[] {new Character(str.charAt(i))}) == null) {}\n" +
" if ((++i) == 0) {}\n" +
" }\n" +
" }\n" +
" }\n" +
"}\n" +
"\n" +
"class Do {\n" +
" public void run(char c) {}\n" +
"}\n" +
"\n" +
"class Invoke {\n" +
" Invoke (Object thiz, String methodName, Class[] paramTypes, Object[] args) {\n" +
" try {\n" +
" if (thiz.getClass().getMethod(methodName, paramTypes).invoke(thiz, args) == null) {}\n" +
" }\n" +
" catch (Exception e) {\n" +
" }\n" +
" }\n" +
"}\n" +
"\n" +
"class ConvertStringToStringReturningClass {\n" +
" ConvertStringToStringReturningClass(String str, StringReceiver sr, final StringBuffer buff) {\n" +
" if (buff.append(\n" +
"\"//Call this method to get the string that represents (most of) the program, except for this\\n\" + \n" +
"\"//class itself.\\n\" + \n" +
"\"class GetProgramString {\\n\" + \n" +
"\" GetProgramString(StringReceiver sr) {\\n\" + \n" +
"\" if (new Invoke(sr, \\\"receive\\\", new Class[] {String.class}, new Object[] {\\n\" +\n" +
"\"\\\"\") == null) {}\n" +
" if (new ForEachChar(str, new Do() {\n" +
" public void run(char c) {\n" +
" if (c == \'\\\"\' || c == \'\\\'\' || c == \'\\\\\') {\n" +
" if (buff.append(\"\\\\\") == null) {}\n" +
" if (buff.append(c) == null) {}\n" +
" }\n" +
" else if (c == \'\\n\') {\n" +
" if (buff.append(\"\\\\n\\\" + \\n\\\"\") == null) {}\n" +
" }\n" +
" else {\n" +
" if (buff.append(c) == null) {}\n" +
" }\n" +
" }}) == null) {}\n" +
" if (buff.append(\n" +
"\"\\\"\\n\" +\n" +
"\" }) == null) {}\\n\" + \n" +
"\" }\\n\" + \n" +
"\"}\\n\") == null) {}\n" +
" if (new Invoke(sr, \"receive\", new Class[] {String.class}, new Object[] {buff.toString()}) == null) {}\n" +
" }\n" +
"}\n" +
"\n" +
"class StringReceiver {\n" +
" public void receive(String str) {}\n" +
"}"
}) == null) {}
}
}
Items of interest in this solution:
- To do a program that does anything, you need statements. The only statements that don't end with a ";" end with a "}". In order to execute an expression statement, you need to put it in the conditional statement of an if (and, for that if, it must be followed by an empty compound statement "{}"). If the type of your expression is not of type boolean, you'll need to nest that subexpression into a larger expression that is of type boolean. For subexpressions with Object value, the best way to do that is to compare it with null. For subexpressions with integral value, you can compare it to 0.
- If you write your own method it'll need to be void. This is because all non-void methods must return something, and the only way to return something (in the traditional way) is to use a return statement. And return statements always need that semicolon. Thus, all methods you write must be void. Similarly, interfaces and abstract classes with abstract methods require semicolons in their definitions, so they are out as well.
- The exception to this rule is when you are writing a constructor. In a sense, a constructor is a kind of method that returns a non-void Object without requiring the use of an explicit return statement.
- It's tricky to call a void method without using a semicolon. The challenge is that you cannot embed a void value as a subexpression of a non-void valued expression. This makes it hard to call System.out.println. The trick used here is to use Java reflection. The invoke method when using reflection always returns an Object, even for void methods.
- Given all of that, the simple call to Print might be better understood now, and the Print class and its constructor is really just what subroutines need to look like.
- Before I said you couldn't return anything. That's still true, but there is a way out: In the program I have two subroutines (again, represented as classes) called GetProgramString and ConvertStringToStringReturningClass. The former takes in no parameters, while the latter takes in a String and a StringBuffer. The StringBuffer is just a temporary variable needed by the subroutine. But, since declaring local variables also requires semicolons, we need to rely on the caller to pass in reasonably initialized values for us. The methods return a String by using the StringReceiver class, which is really only a way for the caller to get the value "returned" by the subroutine bound to a piece of code that can use it.
- In the case of ForEachChar, I abstracted the idea of a for loop. I used the innerclass Body merely as a way to declare that temporary "i" variable. (Because any declaration other than parameter declarations would have needed a semicolon.)
- If you want, change the long string literal in GetProgramString to just "hi" (or, perhaps hi with quotes, slashes and escaped newlines in it as well). Seeing the output of the program with that change makes the whole program easier to understand.
- Indeed, I debugged the program using that "hi" string initially. I then copied the rest of the program, everything but the GetProgramString method, and then a pasted it into an empty string literal in Eclipse. Eclipse has a the nifty feature that when you paste into a string literal it will automatically break it up into multiple lines and escape any characters it needs to.
- If this topic is interesting to you, I highly recommend that you look at lambda calculus for further study. It's a logic used in studying programming languages and will really show you how creative you can be from within a very restricted language.
-Macneil Shonle