The Eclipse formatter enforces many basic style settings, such indentation, placement of opening and closing braces, operator spacing, etc. For our purposes, the default Eclipse formatter settings are modified only slightly, and these settings are included in the OSU CSE Eclipse Workspace Template. This workspace also sets Eclipse to format-on-save, so no further set-up is required.
In addition, Checkstyle is used to audit for violations of coding conventions. These conventions are based on the classic Sun Checks (Eclipse) style, with a few tweaks to better match the programming-in-the-small nature of this course sequence. The Checkstyle configuration is available here: https://cse22x1.engineering.osu.edu/common/OsuCseStyle.xml. Again, the workspace template sets Checkstyle to use this configuration, so no further set-up is required.
Project solutions are expected to be completely free of Checkstyle warnings (unless explicitly stated otherwise in that assignment’s instructions).
Some examples of Checkstyle-enforced rules are:
The following rules should be followed, regardless of whether or not they are flagged by Checkstyle.
Use meaningful, descriptive names for all constants, variables, formal parameters, and methods.
// bad
boolean myFlag = false; // meaningless name
private static int m(int value) { ... } // method and parameter names are not descriptive
static final int DOZEN = 12; // uses the value as a constant's name
// good
boolean isEven = false;
private static int factor(int n) { ... }
static final int CARTON_SIZE = 12;
Use camel case for methods, variables, formal parameters.
That is, start with a lower-case letter and only capitalize the first letter of each following word,
no spaces (e.g., camelCase
).
// bad
private static int sum_of_squares(int n) { ... } // underscores between words
private static void PrintXMLTree(XMLTree t, SimpleWriter out) { ... } // starts with capital
// good
private static int sumOfSquares(int n) { ... }
private static void printXMLTree(XMLTree t, SimpleWriter out) { ... }
Use camel case for local constants, but use screaming snake case for class-level constants.
Screaming snake case consists of all upper-case letters with underscores between words
(e.g., SCREAMING_SNAKE_CASE
).
// bad
final int QUARTER_VALUE = 25; // local constant
static final int maxSize = 100; // class-level constant
// good
final int quarterValue = 25;
static final int MAX_SIZE = 100;
Use inline comments to clarify and enhance your code. Do not include redundant comments that restate what is already obvious in the code. Do not use comments to excuse poor code or poor choice of names.
// bad
int sum = 0; // initialize sum to 0
double x = v[0]; // x will hold the average
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
// good
int sum = 0;
double average = v[0];
/*
* Set cursor to indicate computation on-going; this matters only if
* processing the event might take a noticeable amount of time as seen
* by the user
*/
this.setCursor(Cursor.getPredefinedCursor(Cursor.WAIT_CURSOR));
Document any new methods you design consistently with the way methods provided by us are documented.
This means providing complete Javadoc documentation including the informal description,
@param
for each formal parameter, @return
if the method is a function and returns a value,
parameter modes for all parameters (@clears
, @replaces
, @updates
;
restores-mode is the default when the parameter mode is not specified),
precondition (@requires
) and postcondition (@ensures
).
If need be, also document any aliases your method will create (@aliases
).
// bad
/**
* The sum of part of an array.
*/
private static int prefixSum(int[] a, int n) {
...
}
// good
/**
* The sum of the first n values of an array.
*
* @param a the array of integers
* @param n the length of the prefix to be summed
* @return the sum of the elements in the prefix of a
* @requires 0 <= n < a.length
* @ensures sum = a[0] + a[1] + ... + a[n - 1]
*/
private static int prefixSum(int[] a, int n) {
...
}
Use the equals()
method to compare strings for equality, not ==
.
This is a special case of the more general rule to use equals()
to compare objects for equality.
// bad
found = (s1 == s2);
// good
found = s1.equals(s2);
Avoid using escape sequences to encode newlines in string literals.
Use SimpleWriter
’s println()
to terminate a line or to add an empty line.
// bad
out.print("Hello, World!\n");
// good
out.println("Hello, World!");
Prefer spaces to tab characters in your output.
// bad
out.println("Sequence of numbers:");
for (int i = 0; i < n; i++) {
out.println("\t" + a[i]);
}
// good
out.println("Sequence of numbers:");
for (int i = 0; i < n; i++) {
out.println(" " + a[i]);
}
Declare variables “on demand” when you are ready to initialize them to a meaningful value, not before.
// bad
XMLTree t; // declared without initialization
String name = ""; // guaranteed to be overwritten before being used
out.println("Enter the name of the XML file: ");
name = in.nextLine();
t = new XMLTree(name);
// good
out.println("Enter the name of the XML file: ");
String name = in.nextLine();
XMLTree t = new XMLTree(name);
Declare named constants for every literal that is a magic number (regardless of what Checkstyle says).
// bad
if (d > 7) {
...
}
// good
static final int DAYS_PER_WEEK = 7;
...
if (d > DAYS_PER_WEEK) {
...
}
Do not assign values to formal parameters.
// bad
private static void generateSeries(int n, SimpleWriter out) {
while (n >= 0) {
out.println(n);
n = n - 1;
}
}
// good
private static void generateSeries(int n, SimpleWriter out) {
int i = n;
while (i >= 0) {
out.println(i);
i = i - 1;
}
}
Avoid using ==
and !=
to compare doubles.
// bad
while (d != 1.0) {
d = d + 0.1;
}
// good
while (d < 1.0) {
d = d + 0.1;
}
Use equals()
to check for equality of variables of a reference type (not ==
or !=
).
XMLTree t1 = new XMLTree(source1);
XMLTree t2 = new XMLTree(source2);
// bad
if (t1 == t2) {
...
}
// good
if (t1.equals(t2)) {
...
}
Do not use assignment (=
) to “copy” a variable of a mutable reference type.
Assignment creates an alias, not a copy.
NaturalNumber n = new NaturalNumber2(5);
// bad
NaturalNumber m = n;
// good
NaturalNumber m = new NaturalNumber2(n);
Always use braces for if
, if-else
, while
, for
, etc.
This rule applies even when the body of the block is a single statement.
// bad
if (n < 0) out.println("Negative number");
// good
if (n < 0) {
out.println("Negative number");
}
Do not use postfix increment or decrement (i.e., i++
or i--
) as subexpressions.
Use them only as stand-alone statements (i.e. in place of i = i + 1
and i = i – 1
respectively).
// bad
out.print(i++);
a[j] = a[j--] + 1;
// good
out.print(i);
i++; // or i += 1;
a[j] = a[j] + 1;
j--; // or j -= 1;
Do not use prefix increment or decrement (i.e., ++i
or --i
).
This rule is a consequence of the Postfix Increment/Decrement rule.
// bad
++count;
// good
count++; // or count += 1;
Do not use break
or return
in loops.
As a consequence of this rule, do not use while (true) {..}
.
The motivation for this rule is pedagogical: The goal is to guide students to think about the role of the loop invariant, together with the loop condition, in reasoning about loops.
// bad
while (true) {
...
if (n < 0) break;
...
}
// good
while (n >= 0) {
...
}
Do not exit a loop early by changing the index variable to make the loop condition false.
// bad
for (int i = 0; i < n; i++) {
...
if (/* I need to exit the loop */) {
i = n; // do not use this as a hack to force the loop to exit early
}
...
}
// good
boolean found = false;
while (!found && i < n) {
...
found = /* condition for exiting the loop */;
...
}
In methods that return a value (aka “functions”), use only one return statement.
// bad
if (condition) {
return true;
}
...
return false;
// good
int result = false;
if (condition) {
result = true;
}
...
return result;
In void methods (aka “procedures”), do not use return statements.
// bad
private static void printXMLTreeInfo(XMLTree t, SimpleWriter out) {
if (!t.isTag()) {
out.println("Root is not a tag.");
return;
}
...
}
// good
private static void printXMLTree(XMLTree t, SimpleWriter out) {
if (!t.isTag()) {
out.println("Root is not a tag.");
} else {
...
}
}
Do not duplicate code (e.g., in both branches of an if-else).
// bad
if (n < 0) {
out.println("Negative number");
n = -n;
out.println("The absolute value is " + n); // duplicated code
} else {
out.println("Positive number");
out.println("The absolute value is " + n); // duplicated code
}
// good
if (n < 0) {
out.println("Negative number");
n = -n;
} else {
out.println("Positive number");
}
out.println("The absolute value is " + n);
Simplify boolean expressions by removing unnecessary boolean literals.
// bad
if (a[i] % p == 0) { // branches just assign true or false to found
found = true;
} else {
found = false;
}
odd = ((n % 2 == 0) == false); // equality to false same as negation
// good
found = a[i] % p == 0;
odd = (n % 2 != 0);
Do not use the ternary operator _ ? _ : _
.
For novice programmers, the ternary operator can be difficult to read.
// bad
a[i] % 2 == 0 ? evenCount++ : oddCount++;
// good
if (a[i] % 2 == 0) {
evenCount++;
} else {
oddCount++;
}
Never pass a variable of a mutable reference type as an argument twice or more to a single method call. Remember that the receiver is an argument.
NaturalNumber n, m...
// bad
printGCD(n, n, out);
n.add(n); // repeated argument since receiver is an argument
// good
printGCD(n, m, out);
n.add(m);
(Software 2) No constructor or method body in a kernel class should call any public method from the same component family.
public class NaturalNumber2 extends NaturalNumberSecondary {
/**
* Representation of {@code this}.
*/
private Stack<Integer> digits;
...
@Override
public final int divideBy10() {
int k = 0;
// bad
if (!this.isZero()) { // calls public NaturalNumber method
k = this.digits.pop();
}
// good
if (this.digits.length() > 0) { // uses the representation directly
k = this.digits.pop();
}
return k;
}
...
}