Good Java idioms

Implementing equals()

class Person {
  String name;
  int birthYear;
  
  public boolean equals(Object obj) {
    if (!(obj instanceof Person))
      return false;
    else {
      Person other = (Person)obj;
      return name.equals(other.name)
          && birthYear == other.birthYear;
    }
  }
}

Implementing compareTo()

class Person implements Comparable<Person> {
  String firstName;
  String lastName;
  
  // Compare by firstName, then break ties by lastName
  public int compareTo(Person other) {
    if (firstName.compareTo(other.firstName) != 0)
      return firstName.compareTo(other.firstName);
    else
      lastName.compareTo(other.lastName);
  }
}

Implementing clone()

class Values implements Cloneable {
  int foo;
  int[] bars;
  
  public Values clone() {
    try {
      Values result = (Values)super.clone();
      result.bars = result.bars.clone();
      return result;
    } catch (CloneNotSupportedException e) {  // Impossible
      throw new AssertionError(e);
    }
  }
}

Using Iterator.remove()

void filter(List<String> list) {
  for (Iterator<String> iter = list.iterator(); iter.hasNext(); ) {
    String item = iter.next();
    if (...)
      iter.remove();
  }
}

Using try-finally

Example with I/O stream:

void writeStuff() throws IOException {
  OutputStream out = new FileOutputStream(...);
  try {
    out.write(...);
  } finally {
    out.close();
  }
}

Example with lock:

void doWithLock(Lock lock) {
  lock.acquire();
  try {
    ...
  } finally {
    lock.release();
  }
}

Using StringBuilder/StringBuffer

// join(["a", "b", "c"]) -> "a and b and c"
String join(List<String> strs) {
  StringBuilder sb = new StringBuilder();
  boolean first = true;
  for (String s : strs) {
    if (first) first = false;
    else sb.append(" and ");
    sb.append(s);
  }
  return sb.toString();
}

Staring a thread

Example implementing Runnable:

void startAThread0() {
  new Thread(new MyRunnable()).start();
}

class MyRunnable implements Runnable {
  public void run() {
    ...
  }
}

Example extending Thread:

void startAThread1() {
  new MyThread().start();
}

class MyThread extends Thread {
  public void run() {
    ...
  }
}

Example anonymously extending Thread:

void startAThread2() {
  new Thread() {
    public void run() {
      ...
    }
  }.start();
}

Reversing a String

String reverse(String s) {
  new StringBuilder(s).reverse().toString();
}

Defensive checking: values

int factorial(int n) {
  if (n < 0)
    throw new IllegalArgumentException("Undefined");
  else if (n >= 13)
    throw new ArithmeticException("Result overflow");
  else if (n == 0)
    return 1;
  else
    return n * factorial(n - 1);
}

Defensive checking: objects

int findIndex(List<String> list, String target) {
  if (list == null || target == null)
    throw new NullPointerException();
  ...
}

Defensive checking: array indexes

void frob(byte[] b, int index) {
  if (b == null)
    throw new NullPointerException();
  if (index < 0 || index >= b.length)
    throw new IndexOutOfBoundsException();
  ...
}

Defensive checking: array ranges

void frob(byte[] b, int off, int len) {
  if (b == null)
    throw new NullPointerException();
  if (off < 0 || off > b.length
    || len < 0 || b.length - off < len)
    throw new IndexOutOfBoundsException();
  ...
}

Generating a random integer in a range

Random rand = new Random();

// Between 1 and 6, inclusive
int diceRoll() {
  return rand.nextInt(6) + 1;
}

Packing 4 bytes into an int

int packBigEndian(byte[] b) {
  return b[0] << 24
      | (b[1] & 0xFF) << 16
      | (b[2] & 0xFF) << 8
      |  b[3] & 0xFF;
}

int packLittleEndian(byte[] b) {
  return b[0] & 0xFF
      | (b[1] & 0xFF) << 8
      | (b[2] & 0xFF) << 16
      |  b[3] << 24;
}

Unpacking an int into 4 bytes

byte[] unpackBigEndian(int x) {
  return new byte[] {
    (byte)(x >>> 24),
    (byte)(x >>> 16),
    (byte)(x >>>  8),
    (byte)(x >>>  0)
  };
}

byte[] unpackLittleEndian(int x) {
  return new byte[] {
    (byte)(x >>>  0),
    (byte)(x >>>  8),
    (byte)(x >>> 16),
    (byte)(x >>> 24)
  };
}

Reading from a stream

InputStream in = (...);
while (true) {
  int b = in.read();
  if (b == -1)
    break;
  (... process b ...)
}
in.close();