Java Safari
1 Java Safari

Java Safari

CS 3500, Fall 2015

What the heck does static mean?

What does it mean?

In general programming
  • static: at compile time

  • dynamic: at run time

What does it mean?

Specifically in OO
  • static: belongs to a class

  • dynamic: belongs to an object

(Note that classes are created at compile time and objects at run time)

Where is it used?

  • On fields:

    private int widgetId;
    private static int widgetIdCounter;

    (Mutable static fields are often a bad idea.)

  • On methods:

    public void getWidgetId();
    public static void resetWidgetIdCounter();
  • On classes:

    class SomeClass {
        public static class AssociatedClass { ... }
        ...
    }

What does it mean for a class?

Inner class name is qualified by outer:

SomeClass.AssociatedClass

Everything within the outer class can see each other's stuff:

  • Outer's private names are visible to Nested

  • Nested's private names are visible to Outer

What does it mean for a class?

class Widget {
    private Widget(...) { ... }

    public static class Factory {
        private int widgetIdCounter;

        public Widget create(...) { ... }
    }

    public static Factory factory() { ... }
}

Widget.Factory factObj = Widget.factory();

Widget w1 = factObj.create();
Widget w2 = factObj.create();

When should we use static?

  • Fields: one variable for the whole class rather than one per object

    • Should be rare except for constants

  • Methods: a method associated with the class that doesn't depend on an instance

    • Factory methods are a common case

  • Classes: helper classes with strong associations

    • For example, an iterator class nested in a collection class

Arrays

What are they?

If τ is a Java type, then an array of type τ[] is a

  • mutable,

  • fixed-length,

  • constant-time–indexed

  • sequence of values of type τ.

Let's unpack that.

How can we use them?

Construction
int[] array1 = new int[] { 2, 4, 6, 8 };

int[] array2 = new int[64];

Only works in variable declarations:

int[] array3 = { 2, 4, 6, 8 };

How can we use them?

Length
int[] array1 = new int[] { 3, 4, 5 };

assertEquals( 3, array1.length );



int[] array2 = new int[17];

assertEquals( 17, array2.length );

How can we use them?

Indexing
int[] array1 = new int[] { 3, 4, 5 };

assertEquals( 3, array1[0] );
assertEquals( 4, array1[1] );
assertEquals( 5, array1[2] );
array1[1] = -78;

assertEquals( 3, array1[0] );
assertEquals( -78, array1[1] );
assertEquals( 5, array1[2] );

How can we use them?

Aliasing
int[] a1 = new int[16];
int[] a2 = new int[16];
int[] a3 = a1;
aE( 0, a1[7] ); aE( 0, a2[7] ); aE( 0, a3[7] );
a1[7] = 1;
aE( 1, a1[7] ); aE( 0, a2[7] ); aE( 1, a3[7] );
a2[7] = 2;
aE( 1, a1[7] ); aE( 2, a2[7] ); aE( 1, a3[7] );
a3[7] = 3;
aE( 3, a1[7] ); aE( 2, a2[7] ); aE( 3, a3[7] );

How can we use them?

Testing
int[] a1 = new int[16];
int[] a2 = new int[16];
int[] a3 = a1;
assertEquals( a1, a2 );
assertEquals( a1, a3 );

assertArrayEquals( a1, a2 );

assertArrayEquals( a1, a3 );

How can we use them?

Varargs (and iteration)
public void printInts(int[] intArray) {
    for (int i : intArray) {
        System.out.println(i);
    }
}

printInts(new int[] { 8, 6, 7 });
printInts(8, 6, 7);

How can we use them?

Varargs (and iteration)
public void printInts(int... intArray) {
    for (int i : intArray) {
        System.out.println(i);
    }
}

printInts(new int[] { 8, 6, 7 });
printInts(8, 6, 7);

How can we use them?

Static methods
List<T> Arrays.asList(T... elements);

int Arrays.binarySearch(int[] array, int key);

T[] Arrays.copyOfRange(T[] original, int from, int to);

boolean Arrays.equals(Object[] a1, Object[] a2);

boolean Arrays.deepEquals(Object[] a1, Object[] a2);

See java.util.Arrays for more.

When should we use them?

  • An existing API requires it

  • To ensure sequence length is fixed

  • Efficiency, especially when implementing higher-level data structures

Characters

What are they?

Type char includes:

  • letters ('a', 'b', 'A', 'B', 'α', 'β', etc.)

  • digits ('0', '1', '١', '٢', etc.)

  • punctuation ('.', '-', '«', etc.)

  • whitespace (' ', '\n', '\t', '\v', etc.)

How can we use them?

Some static methods
assertTrue ( Character.isLetter('a') );
assertTrue ( Character.isLetter('β') );
assertFalse( Character.isLetter('8') );

assertFalse( Character.isDigit('a') );
assertFalse( Character.isDigit('β') );
assertTrue ( Character.isDigit('8') );

assertFalse( Character.isLowercase('R') );
assertTrue ( Character.isLowercase('r') );

How can we use them?

As elements of strings
assertEquals( 'a' , "abcde".charAt(0) );
assertEquals( 'c' , "abcde".charAt(2) );

assertEquals(  0, "abcde".indexOf('a') );
assertEquals(  2, "abcde".indexOf('c') );
assertEquals( -1, "abcde".indexOf('f') );

char[] abc = { 'a', 'b', 'c' };
assertEquals(  abc,  "abc".toCharArray() );
assertEquals( "abc", String.valueOf(abc) );

How can we use them?

As elements of strings
assertEquals( 'a' , "abcde".charAt(0) );
assertEquals( 'c' , "abcde".charAt(2) );

assertEquals(  0, "abcde".indexOf('a') );
assertEquals(  2, "abcde".indexOf('c') );
assertEquals( -1, "abcde".indexOf('f') );

char[] abc = { 'a', 'b', 'c' };
assertArrayEquals(  abc,  "abc".toCharArray() );
assertEquals     ( "abc", String.valueOf(abc) );

When should we use them?

Really only two use cases:

  • Processing a string character by character

    • e.g., tokenizing a string into words

  • Representing text-like values that are always of length 1

    • e.g., keystrokes in a GUI

Primitive types versus reference types

What are they?

What are they?

The primitive types
TypeSizeDescriptionRange
boolean1 bittruth valuetrue or false
byte8 bitssigned integer\(-2^7\) to \(2^7 - 1\)
short16-bitssigned integer\(-2^{15}\) to \(2^{15} - 1\)
char16-bitsUnicode character\(0\) to \(2^{16} - 1\)
int32-bitssigned integer\(-2^{31}\) to \(2^{31} - 1\)
float32-bitsfloating point number(see *IEEE 754*)
long64-bitssigned integer\(-2^{63}\) to \(2^{63} - 1\)
double64-bitsfloating point number(see *IEEE 754*)

What are they?

Boxed primitives
PrimitiveBoxed
booleanBoolean
byteByte
shortShort
charCharacter
intInteger
floatFloat
longLong
doubleDouble

What are they?

How can we use them?

You already know how, for the most part.

How can we use them?

How can we use them?

c = f[2];

How can we use them?

c = f[2];

How can we use them?

f[1] = f[0];

How can we use them?

f[1] = f[0];

How can we use them?

f[2] = f[0];

How can we use them?

f[2] = f[0];

How can we use them?

f[3] = f[0];

How can we use them?

f[3] = f[0];

How can we use them?

Equality
long x1 = 7L;
long x2 = 7L;

long y1 = 720_233_830_121_456L;
long y2 = 720_233_830_121_456L;


assertTrue ( x1 == x2 );


assertTrue ( y1 == y2 );

How can we use them?

Equality
Long x1 = 7L;
Long x2 = 7L;

Long y1 = 720_233_830_121_456L;
Long y2 = 720_233_830_121_456L;

assertTrue ( x1.equals(x2) );
assertTrue ( x1 == x2 );

assertTrue ( y1.equals(y2) );
assertFalse( y1 == y2 );

See the docs

How can we use them?

Static versions of Object methods
Double x = 14.573;
double y = 14.573;

assertTrue( x.hashCode() == Double.hashCode(y) );
  • The same for the other boxed types

  • T.compare(t, t), T.toString(t), T.max(t, t), T.max(t, t),

  • Special cases: Double.isNan(double)

When should we use them?

PrimitiveBoxedConsideration
immediatereferenceboxed takes more space and time
no nullnullablewhether null is wanted
no supertypeextends Objectno prims in generic containers
==equals(Object)== might read better
  • Most Java programmers probably prefer primitives

  • This is probably wrong, but it's idiomatic

Equality, physical & logical

Types of equality

  • shallow vs. deep

  • intensional vs. extensional

  • nominal vs. structural

  • physical vs. logical

  • == vs. equals()?

Java equalities

==equals()
shallowas deep or shallow as you like
intensionalintended to be extensional
nominalas nominal or structural as you like
physicallogical, and you choose the logic (so it could be as physical as you like)

You choose the logic

By default: physical equality, a/k/a object identity:

public class Object {
    ...
    public boolean equals(Object obj) {
        return (this == obj);
    }
    ...
}

But we can override...

java.lang.Object source from JDK 8

Overriding equals()

final class Posn {
    ...
    private final int x;
    private final int y;
}

assertEquals( new Posn(2, 4), new Posn(2, 4) );

Overriding equals()

final class Posn {
    ...
    private final int x;
    private final int y;

    public boolean equals(Object obj) {






    }
}

Overriding equals()

final class Posn {
    ...
    private final int x;
    private final int y;

    public boolean equals(Object obj) {
        if (this == obj) return true; // Fast path





    }
}

Overriding equals()

final class Posn {
    ...
    private final int x;
    private final int y;

    public boolean equals(Object obj) {
        if (this == obj) return true; // Fast path

        if (! (object instanceof Posn)) return false;
        Posn that = (Posn) obj;


    }
}

Overriding equals()

final class Posn {
    ...
    private final int x;
    private final int y;

    public boolean equals(Object obj) {
        if (this == obj) return true; // Fast path

        if (! (object instanceof Posn)) return false;
        Posn that = (Posn) obj;

        return  this.x == that.x  &&  this.y == that.y;
    }
}

assertEquals( new Posn(2, 4), new Posn(2, 4) );

Overriding equals()

final class Rational {
    ...
    private final int n;
    private final int d;

    public boolean equals(Object obj) {
        if (this == obj) return true; // Fast path

        if (! (object instanceof Rational)) return false;
        Rational that = (Rational) obj;

        return  this.n == that.n  &&  this.d == that.d;
    }
}

assertEquals( new Rational(2, 4), new Rational(2, 4) );

Overriding equals()

final class Rational {
    ...
    private final int n;
    private final int d;

    public boolean equals(Object obj) {
        if (this == obj) return true; // Fast path

        if (! (object instanceof Rational)) return false;
        Rational that = (Rational) obj;

        return  this.n == that.n  &&  this.d == that.d;
    }
}

assertEquals( new Rational(1, 2), new Rational(2, 4) );

Overriding equals()

final class Rational {
    ...
    private final int n;
    private final int d;

    public boolean equals(Object obj) {
        if (this == obj) return true; // Fast path

        if (! (object instanceof Rational)) return false;
        Rational that = (Rational) obj;

        return  this.n * that.d  ==  this.d * that.n;
    }
}

assertEquals( new Rational(1, 2), new Rational(2, 4) );

Overriding equals()

final class GridCursor {
    ...
    private int r;
    private int c;

    public boolean equals(Object obj) {
        if (! (object instanceof GridCursor)) return false;
        GridCursor that = (GridCursor) obj;

        return  this.r == that.r  &&  this.c == that.c;
    }
}


assertEquals( new GridCursor(2, 4), new GridCursor(2, 4) );

An equality pitfall

while (! cursor.equals(cursor0)) {
    ...

    if (cursor.equals(cursor0)) {
        ...
    }

    ...
}

An equality pitfall

while (! cursor.equals(cursor0)) {
    ...

    // if (cursor.equals(cursor0)) {
    //     ...
    // }

    ...
}

An equality pitfall

while (! cursor.equals(cursor0)) {
    ... cursor.moveLeft(); ...

    // if (cursor.equals(cursor0)) {
    //     ...
    // }

    ...
}

Overriding equals()

final class GridCursor {
    ...
    private int r;
    private int c;

    public boolean equals(Object obj) {
        if (! (object instanceof GridCursor)) return false;
        GridCursor that = (GridCursor) obj;

        return  this.r == that.r  &&  this.c == that.c;
    }
}


assertEquals( new GridCursor(2, 4), new GridCursor(2, 4) );

Overriding equals()

final class GridCursor {
    ...
    private int r;
    private int c;
}


assertNotEquals( new GridCursor(2, 4), new GridCursor(2, 4) );

But wait, there was a problem with some of our equals overrides!

Equality rules

  • The Rules of equals():

    • Reflexivity: x.equals(x)

    • Symmetry: x.equals(y) *iff* y.equals(x)

    • Transitivity: if x.equals(y) and y.equals(z) then x.equals(z)

  • The Rule(s) of hashCode():

    • Compatibility: if x.equals(y) then x.hashCode() == y.hashCode()

    • Non-injectivity: x.hashCode() == y.hashCode() doesn't contradict !x.equals(y)

  • Rule 9 (corollary): "Always override hashCode when you override equals" ---Joshua Bloch, Effective Java 2/E

Overriding equals() and hashCode()

final class GridCursor {
    ...
    private int r;
    private int c;
}

Overriding equals() and hashCode()

final class Posn {
    ...
    private final int x;
    private final int y;

    public boolean equals(Object obj) {
        if (! (object instanceof Posn)) return false;
        Posn that = (Posn) obj;

        return  this.x == that.x  &&  this.y == that.y;
    }




}

Overriding equals() and hashCode()

final class Posn {
    ...
    private final int x;
    private final int y;

    public boolean equals(Object obj) {
        if (! (object instanceof Posn)) return false;
        Posn that = (Posn) obj;

        return  this.x == that.x  &&  this.y == that.y;
    }

    public int hashCode() {
        return Objects.hash(x, y);
    }
}

Overriding equals() and hashCode()

final class Rational {
    ...
    public boolean equals(Object obj) {
        if (! (object instanceof Rational)) return false;
        Rational that = (Rational) obj;

        return  this.n * that.d  ==  this.d * that.n;
    }
    public int hashCode() {
        return Objects.hash(n, d);  // WRONG
    }
}
assertEquals( new Rational(1, 2).hashCode(),
              new Rational(2, 4).hashCode());

Overriding equals() and hashCode()

final class Rational {
    ...
    public boolean equals(Object obj) {
        if (! (object instanceof Rational)) return false;
        Rational that = (Rational) obj;

        return  this.n * that.d  ==  this.d * that.n;
    }
    public int hashCode() {
        int gcd = gcd(n, d);
        return Objects.hash(n / gcd, d / gcd);
    }
}
assertEquals( new Rational(1, 2).hashCode(),
              new Rational(2, 4).hashCode());

Unlearning Fundies 2

Not all of it!

Just a few things

On null

I call it my billion-dollar mistake. It was the invention of the null reference in 1965. At that time, I was designing the first comprehensive type system for references in an object oriented language (ALGOL W). My goal was to ensure that all use of references should be absolutely safe, with checking performed automatically by the compiler. But I couldn't resist the temptation to put in a null reference, simply because it was so easy to implement. This has led to innumerable errors, vulnerabilities, and system crashes, which have probably caused a billion dollars of pain and damage in the last forty years.

—Sir Tony Hoare (inventor of quicksort, Hoare logic, and Communicating Sequential Processes)

Rehabilitating null

Then: null is evil

Now: null is a pain in the ass

How to survive null

Treat it with respect

  • Use it to represent the absence of a reference

  • Conscientiously and sparingly!

  • Carefully document where it's allowed

  • Check for it and fail fast

Rehabilitating ==

Then: don't use ==

Now: understand what == means and use it appropriately

Remember: == compares the immediate contents of variables (simple boxes)

Rehabilitating instanceof

...

Then: instanceof is evil

Now: instanceof is evil

Rehabilitating instanceof

(but not much)

Then: instanceof is evil

Now: instanceof is evil, but we have to use it anyway to override equals() properly, so just hold your nose, use it in equals(), and let's not speak of it again.

The switch statement

What is it for?

// Don't write this

if (c == 's') {
    handleSeconds(false);
}
else if (c = 'S') {
    handleSeconds(true);
}
else if (c = 'm') {
    handleMinutes(false);
}
else if (c = 'M') {
    handleMinutes(true);
}
else {
    unrecognized();
}

What is it for?

// Don't write this:         Do write this:
                             switch (c) {
if (c == 's') {                case 's':
    handleSeconds(false);        handleSeconds(false);
}                                break;
else if (c = 'S') {            case 'S':
    handleSeconds(true);         handleSeconds(true);
}                                break;
else if (c = 'm') {            case 'm':
    handleMinutes(false);        handleMinutes(false);
}                                break;
else if (c = 'M') {            case 'M':
    handleMinutes(true);         handleMinutes(true);
}                                break;
else {                         default:
    unrecognized();              unrecognized();
}                            }

When should we use it?

A multi-way branch that dispatches on a single value

Caveats:

  • Only works on primitive types and (as of Java 7) String

  • Falls through if you omit break:

    case 's':
      handleSeconds(false);
    case 'S':
      handleSeconds(true);
      break;

    For 's' this will call handleSeconds(false) and then handleSeconds(true)

Enumerations

What are they?

  • A special kind of class

  • Creates a finite set of named values

  • Represents a small, fixed set of options

What are they?

enum Status {
  Playing, Stalemate, Won
}

enum Binop {
  Add, Sub, Mul, Div, Pow
}

enum Weekday {
  Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday
}

How can we use them?

double performBinop(Binop op, double a, double b) {
  switch (op) {
    case Add: return a + b;
    case Sub: return a - b;
    case Mul: return a * b;
    case Div: return a / b;
    case Pow: return Math.pow(a, b);
    default:
      throw new RuntimeException("performBinop: default");
  }
}

assertEquals( 8.0, performBinop(Binop.Mul, 2.0, 4.0) );

What are they?

enum UsCoin {
  Penny(1), Nickel(5), Dime(10), Quarter(25), HalfDollar(50);
  private final int cents;
  public int getCentsValue() {
    return cents;
  }
  UsCoin(int cents) {
    this.cents = cents;
  }
}

When should we use them?

  • To represent values in a finite set of options

  • The options are known at compile time

  • The options aren't too many to list

  • No need to extend another class

Exceptions

What are they?

No exceptions: C-style error handling
    int fd, result;

    fd = open(path, flags);
    if (fd == -1) goto cleanup;

    result = write(fd, buf1, size1);
    if (result == -1) goto cleanup;

    result = write(fd, buf2, size2);
    if (result == -1) goto cleanup;

    ...
cleanup:
    // cleanup code

What are they?

Java exceptions
try {
  int fd = open(path, flags);
  write(fd, buf1, size1);
  write(fd, buf2, size2);
  ...
} catch (IOException e) {
  // cleanup code
}

What are they?

Two kinds of exceptions
CheckedUnchecked
extends Exceptionextends Error or RuntimeException
possibly recoverableprobably bail out
e.g., network errore.g., programming error
must appear in throw clausesmay appear in throw clauses

How can we use them?

Basics

Extend:

class IllegalMoveException extends IllegalStateException { ... }

Throw:

throw new IllegalMoveException(reason);

Catch:

try { somethingThatMightThrow(); }
catch (IllegalMoveException e) {
    logError(e);
}

How can we use them?

Checked exceptions

IOException is a checked exception, so it appears in method signatures:

void myBigIoMethod(...) throws IOException {
    ... new FileReader(someFile) ...
}

Methods that call a method that throws and don't catch it also throw:

void innocuousLookingMethod() throws IOException {
  myBigIoMethod(3, true);
}

How can we use them?

Checked exceptions

To discard a throws clause, use catch:

void anotherMethod() {
  try {
    innocuousLookingMethod()
  } catch (IOException e) {
    throw new RuntimeException(e);
  }
}

When should we use them?

  • Throw exceptions to indicate exceptional conditions

    • You know them when you see them

  • Catch exceptions where you're preared to handle exceptional conditions

    • Don't catch-and-ignore—that hides bugs

Generics, or raw types considered harmful

What are they?

Generic classes:

class BinTree<T> { ... }

Generic methods:

<T> void permute(List<T>);

How can we use them?

Generic classes

When it's a type, use a parameter:

BinTree<Widget> = new BinTree<Widget>();

When it's a constructor, <> asks Java to guess:

BinTree<Widget> tree = new BinTree<>();

When using a static member, don't use a parameter:

if (h < BinTree.MAX_HEIGHT) {
    return BinTree.singleton(value);
}

How can we use them?

Generic methods

Typically, Java can infer the type parameter:

permutation.permute(widgetList);

When it can't, you can tell it:

permutation.<Widget>permute(Arrays.asList());

A warning

Java allows omitting the parameter(s) on types and constructors:

BinTree tree = new BinTree();

This is called a raw type.

It's (almost) always wrong.

Why have a feature that’s so bad that no one should use it, ever?

Backward compatibility

  • Java hasn't always had generics

  • It has raw types so pre-generic code can still work

The bad old days

List widgetList = new ArrayList();

widgetList.add(thisWidget);
widgetList.add(thatWidget);

The List.get method returns type Object:

widgetList.get(i).getWidgetId();

So you need to cast:

((Widget) widgetList.get(i)).getWidgetId();

Reliving the bad old days

Java still allows raw types (like on the previous slide)

What you get for it:

  • Casts everywhere

  • Class cast exceptions when you mess it up

  • Unchecked casts that result in an exception later

What if I want a list where I don't know the element type?

Use a wildcard: List<?>

When should we use raw types?

When should we use raw types?

Never.

I know of no Java expert who disagrees.