Vector
ClassVector
class:
class Vector { Vector(int capacity) { this.capacity = capacity; arr = new int[capacity]; } Vector() {this(DEFAULT_CAPACITY);} int get(int index) {return arr[index];} void set(int index, int value) {arr[index] = value;} void add(int value) { arr[size] = value; size++; } int getCapacity() {return capacity;} int size() {return size;} public String toString() { String result = "{"; for (int i = 0; i < size; i++) result += arr[i] + (i < size-1 ? ", " : ""); result += "}"; return result; } int [] arr; int capacity, size = 0; static final int DEFAULT_CAPACITY = 10; }
I had mentioned in the previous lecture that this code was actually incorrect, and that we would correct the situation in this lecture. There are actually two error in our class:
0 … size-1
. Elements outside of that range are
not part of the container (even though they exist within the underlying container).
Vector
and then added an additional element, an ArrayIndexOutOfBoundsException
WOULD occur
throw Exception
, but we'd rather be consistent with how Java handles this situation.
catch Exception e)
in our try/catch
block which is
way too general a catch.
String
s, and various other containers in Java
ArrayIndexOutOfBoundsException
String
-based bound violations generate a StringIndexOutOfBoundsException
IndexOutOfBoundsException
ArrayList
does not have an ArrayListIndexOutOfBoundsException
but rather throws the
general IndexOutOfBoundsException
VectorIndexOutOfBoundsException
, but will follow the ArrayList
approach
Vector
is closer in some sense to ArrayList
than to
String
: which is not strictly a container in the Java universe
Exception
or create our own exception class.
IllegalStateException
when they are full, so we'll do the same
class Vector { Vector(int capacity) {arr = new int[capacity];} Vector() {this(DEFAULT_CAPACITY);} int get(int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index " + index + " out of bounds for length " + size); return arr[index]; } void set(int index, int value) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index " + index + " out of bounds for length " + size); arr[index] = value; } void add(int value) { if (size == getCapacity()) throw new IllegalStateException("Exceeded vector capacity of " + arr.length); arr[size++] = value; } int getCapacity() {return arr.length;} int size() {return size;} public String toString() { String result = "{"; for (int i = 0; i < size; i++) result += arr[i] + (i < size-1 ? ", " : ""); result += "}"; return result; } int [] arr; int size = 0; static final int DEFAULT_CAPACITY = 10; }
get
and set
methods
IndexOutOfBoundsException
constructor — like most exception class constructor — accepts a
String
containing some sort of descriptive message.
add
method … the only method that increases the size of the container.
getCapacity
in the overflow check in add
. While we could have used arr.length
, we choose to use the method instead for
a couple of reasons:
.length
would no
longer be applicable; however, we can always change the logic of getCapacity
to reflect whatever we move to.
public class VectorApp { public static void main(String [] args) { Vector v = new Vector(); System.out.println("capacity: " + v.getCapacity()); System.out.println("size: " + v.size()); try { System.out.println(v.get(0)); } catch (IndexOutOfBoundsException e) { System.out.println("Successfully caught exception on get: " + e); } try { v.set(0, 999); } catch (IndexOutOfBoundsException e) { System.out.println("Successfully caught exception on set: " + e); } for (int i = 0; i < v.getCapacity(); i++) v.add(i * 10); System.out.println(v); System.out.println("size: " + v.size()); for (int i = 0; i < v.size(); i++) v.set(i, v.get(i)+1); System.out.println(v); System.out.println(); try { v.add(999); } catch (IllegalStateException e) { System.out.println("Successfully caught exception on add: " + e); } System.out.println("capacity: " + v.getCapacity()); System.out.println("size: " + v.size()); System.out.println(v); } }
try
/catch
block; the basic logic is:
get(0)
on the empty Vector
)
try/catch
catch
portion, we print out the successful catch of the exception
try
portion —
after the exception-cause statement, signalling the error that an exception was not thrown.
set
capacity: 10 size: 0 Successfully caught exception on get: java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0 Successfully caught exception on set: java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0 {0, 10, 20, 30, 40, 50, 60, 70, 80, 90} size: 10 {1, 11, 21, 31, 41, 51, 61, 71, 81, 91} Successfully caught exception on add: java.lang.IllegalStateException: Exceeded vector capacity of 10 capacity: 10 size: 10 {1, 11, 21, 31, 41, 51, 61, 71, 81, 91}
throw
statement), must be a subclass (direct or otherwise)
the Exception
class.
super
.java
file or as a nested class
of the class for which it will be used to handle exceptions. We will illustrate both.
RunTimeException
— Checked vs Unchecked ExceptionsScanner
object), there is is possibility that the file may not be found
and a FileNotFoundException will be thrown
throws FileNotFoundException
(or simply throws Exception
in this situation
a[i]
, or the expression a / b
ArrayIndexOutOfBoundsException
to be thrown, and the latter can cause an ArithmeticException
(with a 'zero divide' message).
throws
clause is required
FileNotFoundExceptioa
is a perfect example of this. The existence of the file is out of the control of the programmer, but
if the file doesn't exist, the programmer can take steps to recover, by asking for the name of a different file, and this logic can
be placed in a try/catch
enclosing the opening of the file
throws ArrayIndexOutOfBoundsException
on every method that uses an array; or have a Exception
are by default checked. To specify an exception class represents an unchecked exception (which again means that no
throws
clause is necessary), one make the class a subclass of RuntimeException
RuntimeException
is itself a subclass of Exception
so the class in question is indeed an exception class
RuntimeException
makes the class an unchecked exception class
RuntimeException
is actually a quite good name for the 'class' of unchecked exceptions. Runtime errors are usually the result of programming errors (as
opposed to something like a FileNotFoundException
.
Vector
are the result of programming errors and or situations that cannot be recovered from:
get
or set
sending an invalid index. This is almost always the result of a programming
error; furthermore it's probably unrecoverable or the bad index would not have been sent in the first place.
Vector
with a larger capacity should have been created (for dynamic fixed-size) or
As such, we will make our VectorException
class an unchecked exception.
Exception
class
class VectorException extends RuntimeException { VectorException(String message) {super(message);} }
Exception
, and in our case, we
want it to be an unchecked as well, therefore were subclas it from RuntimeException
.
Vector
class:
class Vector { Vector(int capacity) {arr = new int[capacity];} Vector() {this(DEFAULT_CAPACITY);} int get(int index) { if (index < 0 || index >= size) throw new VectorException("Index " + index + " out of bounds for length " + size); return arr[index]; } void set(int index, int value) { if (index < 0 || index >= size) throw new VectorException("Index " + index + " out of bounds for length " + size); arr[index] = value; } void add(int value) { if (size == getCapacity()) throw new VectorException("Exceeded vector capacity of " + arr.length); arr[size++] = value; } int getCapacity() {return arr.length;} int size() {return size;} public String toString() { String result = "{"; for (int i = 0; i < size; i++) result += arr[i] + (i < size-1 ? ", " : ""); result += "}"; return result; } int [] arr; int size = 0; static final int DEFAULT_CAPACITY = 10; }
public class VectorApp { public static void main(String [] args) { Vector v = new Vector(); System.out.println("capacity: " + v.getCapacity()); System.out.println("size: " + v.size()); try { System.out.println(v.get(0)); } catch (VectorException e) { System.out.println("Successfully caught exception on get: " + e); } try { v.set(0, 999); } catch (VectorException e) { System.out.println("Successfully caught exception on set: " + e); } for (int i = 0; i < v.getCapacity(); i++) v.add(i * 10); System.out.println(v); System.out.println("size: " + v.size()); for (int i = 0; i < v.size(); i++) v.set(i, v.get(i)+1); System.out.println(v); System.out.println(); try { v.add(999); } catch (VectorException e) { System.out.println("Successfully caught exception on add: " + e); } System.out.println("capacity: " + v.getCapacity()); System.out.println("size: " + v.size()); System.out.println(v); } }
capacity: 10 size: 0 Successfully caught exception on get: VectorException: Index 0 out of bounds for length 0 Successfully caught exception on set: VectorException: Index 0 out of bounds for length 0 {0, 10, 20, 30, 40, 50, 60, 70, 80, 90} size: 10 {1, 11, 21, 31, 41, 51, 61, 71, 81, 91} Successfully caught exception on add: VectorException: Exceeded vector capacity of 10 capacity: 10 size: 10 {1, 11, 21, 31, 41, 51, 61, 71, 81, 91}
static
keyword in the class header (e.g. static class …
)
class Vector {
class VectorException extends RuntimeException {
VectorException(String message) {super(message);}
}
Vector(int capacity) {arr = new int[capacity];}
Vector() {this(DEFAULT_CAPACITY);}
int get(int index) {
if (index < 0 || index >= size) throw new VectorException("Index " + index + " out of bounds for length " + size);
return arr[index];
}
void set(int index, int value) {
if (index < 0 || index >= size) throw new VectorException("Index " + index + " out of bounds for length " + size);
arr[index] = value;
}
void add(int value) {
if (size == getCapacity()) throw new VectorException("Exceeded vector capacity of " + arr.length);
arr[size++] = value;
}
int getCapacity() {return arr.length;}
int size() {return size;}
public String toString() {
String result = "{";
for (int i = 0; i < size; i++)
result += arr[i] + (i < size-1 ? ", " : "");
result += "}";
return result;
}
int [] arr;
int size = 0;
static final int DEFAULT_CAPACITY = 10;
}
static
in the nested class header means we are dealing with an inner (non-static nested) class
Vector$VectorException
java.lang.String
package statement
at the top of your source file).
capacity: 10 size: 0 Successfully caught exception on get: Vector$VectorException: Index 0 out of bounds for length 0 Successfully caught exception on set: Vector$VectorException: Index 0 out of bounds for length 0 {0, 10, 20, 30, 40, 50, 60, 70, 80, 90} size: 10 {1, 11, 21, 31, 41, 51, 61, 71, 81, 91} Successfully caught exception on add: Vector$VectorException: Exceeded vector capacity of 10 capacity: 10 size: 10 {1, 11, 21, 31, 41, 51, 61, 71, 81, 91}
new
(they are similar to classes in this respect; but different in that they cannot have methods)
Here is the core logic for resizing an array, assuming the array is referenced by the variable arr
:
new
expression to some temporary reference variable; we'll call it temp
arr
) to the corresponding elements new array (referenced by temp
)
temp
to arr
)
and here is the corresponding Java fragment:
int [] temp = new int[2*arr.length]; for (int i = 0; i < arr.length; i++) temp[i] = arr[i]; arr = temp;
arr.length
, i.e., the old array objects length
int [] resize(int [] arr) { int [] temp = new int[2*arr.length]; for (int i = 0; i < arr.length; i++) temp[i] = arr[i]; return temp; }
int [] arr = new int[…];
…
// We realize we need a bigger array
arr = resize(arr);
void resize(int [] arr) { int [] temp = new int[2*arr.length]; for (int i = 0; i < arr.length; i++) temp[i] = arr[i]; arr = temp; }
int [] arr = new int[…];
…
// We realize we need a bigger array
resize(arr);
class Vector { Vector() { arr = new int[INITIAL_CAPACITY]; } int get(int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index " + index + " out of bounds for length " + size); return arr[index]; } void set(int index, int value) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index " + index + " out of bounds for length " + size); arr[index] = value; } void add(int value) { checkCapacity(); arr[size++] = value; } int getCapacity() {return arr.length;} int size() {return size;} public String toString() { String result = "{"; for (int i = 0; i < size; i++) result += arr[i] + (i < size-1 ? ", " : ""); result += "}"; return result; } private void checkCapacity() { if (size < getCapacity()) return; int [] temp = new int[2*getCapacity()]; for (int i = 0; i < size; i++) temp[i] = arr[i]; arr = temp; } int [] arr; int size = 0; static final int INITIAL_CAPACITY = 10; }
INITIAL_CAPACITY
to start things off, but the container will resize itself as necessary
capacity
instance variable is required, we obtain the capacity from arr.length
add
)
checkCapacity
compares size
to the
capacity of the container, and returns immediately if nothing need be done.
checkCapacity
is declared private
as the memory/capacity management is solely the
responsibility of the container.
getCapacity
rather than arr.length
for both
increased readability as well as to 'protect' against any possibly change in the underlying container.
public class VectorApp { public static void main(String [] args) { basics(); exceptions(); capacityManagement(); } static void basics() { System.out.println("=== Basic Functionality ==="); System.out.println("--- Creating Vector and printing it and its initial stats ---"); Vector v = new Vector(); System.out.println(v + " (" + v.size() + "/" + v.getCapacity()); System.out.println(); System.out.println("--- Loading it up to capacity; add ---"); for (int i = 0; i < v.getCapacity(); i++) { v.add(i * 10); System.out.println(v + " (" + v.size() + "/" + v.getCapacity() + ")"); } System.out.println(); System.out.println("--- Incrementing each element; get and set ---"); for (int i = 0; i < v.size(); i++) v.set(i, v.get(i)+1); System.out.println(v); System.out.println(); } static void exceptions() { System.out.println("=== Exceptions ==="); Vector v = new Vector(); try { System.out.println(v.get(0)); } catch (IndexOutOfBoundsException e) { System.out.println("Successfully caught exception on get: " + e); } try { v.set(0, 999); } catch (IndexOutOfBoundsException e) { System.out.println("Successfully caught exception on set: " + e); } System.out.println(); } static void capacityManagement() { System.out.println("=== Capacity Management ==="); Vector v = new Vector(); System.out.println("--- Creating Vector and loading it up with 1,000 elements ---"); int oldCapacity = -1; for (int i = 0; i < 1000; i++) { v.add(i); if (v.getCapacity() != oldCapacity) if (v.size() <= 10) System.out.println(v + " (" + v.size() + "/" + v.getCapacity() + ")"); else System.out.println("{" + v.get(0) + ", " + v.get(1) + "..." + v.get(v.size()-2) + ", " + v.get(v.size()-1) + "} (" + v.size() + "/" + v.getCapacity() + ") ... resized"); oldCapacity = v.getCapacity(); } System.out.println("{" + v.get(0) + ", " + v.get(1) + "..." + v.get(v.size()-2) + ", " + v.get(v.size()-1) + "} (" + v.size() + "/" + v.getCapacity() + ")"); } }
try/catch
block for
overflow (IllegalStateException
)
=== Basic Functionality === --- Creating Vector and printing it and its initial stats --- {} (0/10 --- Loading it up to capacity; add --- {0} (1/10) {0, 10} (2/10) {0, 10, 20} (3/10) {0, 10, 20, 30} (4/10) {0, 10, 20, 30, 40} (5/10) {0, 10, 20, 30, 40, 50} (6/10) {0, 10, 20, 30, 40, 50, 60} (7/10) {0, 10, 20, 30, 40, 50, 60, 70} (8/10) {0, 10, 20, 30, 40, 50, 60, 70, 80} (9/10) {0, 10, 20, 30, 40, 50, 60, 70, 80, 90} (10/10) --- Incrementing each element; get and set --- {1, 11, 21, 31, 41, 51, 61, 71, 81, 91} === Exceptions === Successfully caught exception on get: java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0 Successfully caught exception on set: java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0 === Capacity Management === --- Creating Vector and loading it up with 1,000 elements --- {0} (1/10) {0, 1...9, 10} (11/20) ... resized {0, 1...19, 20} (21/40) ... resized {0, 1...39, 40} (41/80) ... resized {0, 1...79, 80} (81/160) ... resized {0, 1...159, 160} (161/320) ... resized {0, 1...319, 320} (321/640) ... resized {0, 1...639, 640} (641/1280) ... resized {0, 1...998, 999} (1000/1280)
Vector(int capacity) {arr = new int[capacity];}
Vector v = new Vector(1000000); // avoids a bunch of resizing
Vector
with Integer
Element Typeint
element type of the Vector
with the corresponding Integer
wrapper class.
Object
, so we'll move a step in that
direct and use Integer
as it is a class, and it is also related to int
(i.e., it is its wrapper class).
class Vector { Vector() { this.capacity = INITIAL_CAPACITY; arr = new Integer[capacity]; } Integer get(int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index " + index + " out of bounds for length " + size); return arr[index]; } void set(int index, Integer value) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index " + index + " out of bounds for length " + size); arr[index] = value; } void add(Integer value) { checkCapacity(); arr[size++] = value; } int getCapacity() {return capacity;} int size() {return size;} public String toString() { String result = "{"; for (int i = 0; i < size; i++) result += arr[i] + (i < size-1 ? ", " : ""); result += "}"; return result; } private void checkCapacity() { if (size < capacity) return; capacity *= 2; Integer [] temp = new Integer[capacity]; for (int i = 0; i < size; i++) temp[i] = arr[i]; arr = temp; } Integer [] arr; int capacity, size = 0; static final int INITIAL_CAPACITY = 10; }
public class VectorApp { public static void main(String [] args) { Vector v = new Vector(); System.out.println("capacity: " + v.getCapacity()); System.out.println("size: " + v.size()); try { System.out.println(v.get(0)); } catch (IndexOutOfBoundsException e) { System.out.println("Successfully caught exception on get: " + e); } try { v.set(0, 999); } catch (IndexOutOfBoundsException e) { System.out.println("Successfully caught exception on set: " + e); } for (int i = 1; i <= v.getCapacity(); i++) v.add(i * 10); System.out.println(v); System.out.println("size: " + v.size()); for (int i = 0; i < v.size(); i++) v.set(i, v.get(i)+1); System.out.println(v); v.add(999); System.out.println("capacity: " + v.getCapacity()); System.out.println("size: " + v.size()); System.out.println(v); System.out.println(); } }
int
-related literals or expressions,appearing in a context in
which an Integer
is expected.
Integer
is a wrapper class for int
it is not an int
+
or *
operator defined for Integer
Integer
objects to wrap any int
s when adding to the Vector
(whose element type is Integer
Integer
from an int
(say the int
variable i
), there would
be an Integer
constructor that accepts an int
, and then one could write
Integer integer = new Integer(i);however, for various technical reasons, that is not recommended; rather, there is a static method of
Integer
, valueOf
,
that accepts an int
and returns a new Integer
that wraps that int
, and thus one would write:
Integer integer = Integer.valueOf(i);
int
from any Integer
objects when getting any element from the Vector
and
performing any arithmetic (or other int
-based action on them.
Integer
, intValue
that returns the (wrapped) value of
the receiving Integer
object as an int
, e.g.:
int i = Integer.intValue();
public class VectorApp { public static void main(String [] args) { Vector v = new Vector(); System.out.println("capacity: " + v.getCapacity()); System.out.println("size: " + v.size()); try { System.out.println(v.get(0)); } catch (IndexOutOfBoundsException e) { System.out.println("Successfully caught exception on get: " + e); } try { v.set(0, Integer.valueOf(999)); } catch (IndexOutOfBoundsException e) { System.out.println("Successfully caught exception on set: " + e); } for (int i = 1; i <= v.getCapacity(); i++) v.add(Integer.valueOf(i * 10)); System.out.println(v); System.out.println("size: " + v.size()); for (int i = 0; i < v.size(); i++) v.set(i, Integer.valueOf(v.get(i).intValue()+1)); System.out.println(v); v.add(Integer.valueOf(999)); System.out.println("capacity: " + v.getCapacity()); System.out.println("size: " + v.size()); System.out.println(v); System.out.println(); } }
Integer integer = v.get(i); int j = integer.intValue(); v.set(i, Integer.valueOf(j+1));
Java 5 introduced a new language feature; one that had been long awaited and addressed the above issue: autoboxing.
The basic idea was to make the boundary between a primitive type and its wrapper class as transparent as possible. so that one could (almost) used
int
and Integer
interchangeably.
To do that the compiler was enlisted to do much of the above cumbersome wrapping and unwrapping logic. The wrapping was called boxing, and the unrpapping unboxing.
Vector v = new Vector(); v.add(3); // compiler automatically inserts anInteger.valueOf
// so this internally becomesv.add(Integer.valueOf(3));
Vector v = new Vector();
…
int i = v.get(0); // compiler automatically insert a '.intValue'
// so this becomes int i = v.get(0).intValue();
Vector
in our app:
v.set(i, v.get(i)+1);
v
is retrieved, v.get(i)
Integer
since the Vector
element type is Integer
int
value 5
1
(v.get(i)+1
) added to it
Integer
object we just retrieved, therefore the compiler
unboxes the object, extracting the int
value (5
).
5
is added to 1
resulting in the int
value 6
.
set
method, which is putting the new update value back into the same location of v
.
That value is supposed to be an Integer
(i.e., object) but is the int
(6
) from the previous step
int
into an Integer
object (sing valieOf
and that object then becomes the
value that is stored into v
by the set
.
int
to Integer
Vector
With Object
Element TypeObject
as the element type
Object
is chosen because all classes are descendants of Object
, and an object of any class can be a Vector
element.
class Vector { Vector() { this.capacity = INITIAL_CAPACITY; arr = new Object[capacity]; } Object get(int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index " + index + " out of bounds for length " + size); return arr[index]; } void set(int index, Object value) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index " + index + " out of bounds for length " + size); arr[index] = value; } void add(Object value) { checkCapacity(); arr[size] = value; size++; } int getCapacity() {return capacity;} int size() {return size;} public String toString() { String result = "{"; for (int i = 0; i < size; i++) result += arr[i] + (i < size-1 ? ", " : ""); result += "}"; return result; } private void checkCapacity() { if (size < capacity) return; capacity *= 2; Object [] temp = new Object[capacity]; for (int i = 0; i < size; i++) temp[i] = arr[i]; arr = temp; } Object [] arr; int capacity, size = 0; static final int INITIAL_CAPACITY = 10; }
public class VectorApp {
public static void main(String [] args) {
Vector v = new Vector();
System.out.println("capacity: " + v.getCapacity());
System.out.println("size: " + v.size());
try {
System.out.println(v.get(0));
} catch (IndexOutOfBoundsException e) {
System.out.println("Successfully caught exception on get: " + e);
}
try {
v.set(0, 999);
} catch (IndexOutOfBoundsException e) {
System.out.println("Successfully caught exception on set: " + e);
}
for (int i = 1; i <= v.getCapacity(); i++)
v.add(i * 10);
System.out.println(v);
System.out.println("size: " + v.size());
for (int i = 0; i < v.size(); i++)
v.set(i, ((Integer)v.get(i))+1);
System.out.println(v);
v.add(999);
System.out.println("capacity: " + v.getCapacity());
System.out.println("size: " + v.size());
System.out.println(v);
System.out.println();
}
}
v.get(i)
, now that the element type is Object
all we can say when we extract an element is that it is an Object
Integer
(other than the fact that we know we have only added Integer
s
to the Vector
; this last fact will prove crucial in a bit)
Integer
and thus cannot do any unboxing
Object
; it is only at runtime that the actual element is created (as an
Integer
)
v.add(i * 10);
add
methods is expecting an object of type Object
but is presented with an int
(i * 10
) and thus boxes the result of the expression the corresponding wrapper
class (Integer
) and it is this object that is added to the vector (i.e., the underlying array).
Integer
into an arrays of Object
element type, as Integer
is
a subclass (indirectly possibly) of Object
, i.e., it is a descendant of Object
and thus is-a
object of the suoerclass as well (i.e., an Integer
object is-a Object
object as well by virtue of the
inheritance.
set
(and the add
) are not problematic; Integer
objects are Object
objects as
well, can appear wherever an Object
can, and thus can be made an element of an array with an Object
element type.
Integer
) to a variable higher up in the hioerarcht (e.g. Object
is called upcasting and is always legal and requires no special syntax..
Object
as element type is never a problem.
Object
as their element type
Integer
) to the element so it can be used when assigning the reference to an object from a variable higher up in the hierarchy to a variable lower down.
Object
there are very few methods one can apply; in particular for our case, we cannot invoke the intValue
method to extract the
wrapped int
Object
to a variable lower down the hierarchy (Integer
).
Shape
and two subclasses, Circle
and Square
:
abstract class Shape {…} class Circle extends Shape {…} class Square extends Shape {…}
Now let's start working with some variables and objects of these classes.
1. Circle circle = new Circle(); 2. Square square = new Square(); 3. Shape shape1 = circle; // ok, upcast — All Circles are Shapes by virtue of inheritance 4. Shape shape2 = square; // ok, upcast — All Squares are Shapes
circle
: a reference to a Circle
square
: a reference to a Square
shape1
/ shape2
: reference to a Shape
Circle
created in line 1
Square
created in line 2
new
operator).
Circle
(circle
)
containing a reference to a Circle
object
Square
(square
)
containing a reference to a Square
object
Shape
(shape1
)
containing a reference to a Circle
object
Shape
(shape2
)
containing a reference to a Square
object
circle
/ square
to shape1
/ shape2
respectively
Circle circle = new Circle();
Square square = circle; // illegal -- no Circles are Squares
and
Square square = new Square();
Circle circle = square; // illegal -- no Squares are Circles
Shape shape = new Circle(); // this is a legal upcast … All Circles are Shapes … Circle circle = shape1; // seems ok -- assigning a Circle object to a Circle variable Square square = shape1; // seems wrong -- assigning a Circle object to a Square variable
and
Shape shape = new Square(); // this is a legal upcast … All Circles are Shapes … Circle circle = shape; // seems wrong -- assigning a Square object to a Circle variable Square square = shape1; // seems ok -- assigning a Square object to a Square variable
shape
is to a variable of a subclass (circle
or square
).
Such an assignment is called a downcast (casting down the hierarchy)
shape
…
between the object creation and subsequent assignments is meant to signify that there may be a significant
amount of code executed between the creation of the object and its assignment to circle
and square
shape
to circle
and square
?
Random r = new Random();
boolean b = r.nextBoolean();
Shape shape;
if (b)
shape = new Circle(); // upcast
else
shape = new Square(); // upcast
Circle circle = shape; // ok? or not? downcast
shape
depends on the value of the random variable which is not generated until runtime.
circle
is assigned a reference to a Circle
object (legal) or a reference to
a Square
object (illegal) cannot be determined until runtime
Shape shape = new Circle();
Circle circle = (Circle)shape; // will pass compilation and succeed at runtime
Square square = (Square)shape;; // will pass compilation but fail at runtime with a ClassCastException
Returning to our Random
example
for final emphasis; the following code will sometimes work and sometimes fail with
a ClassCastException
:
Random r = new Random();
boolean b = r.nextBoolean();
Shape shape;
if (b)
Shape = new Circle();
else
Shape = new Square();
Circle circle = (Circle)shape; // will sometimes succeed and sometimes fail at runtime
In summary, downcasts need to be checked at runtime, and if they fail, they throw a ClassCastException
Object
as the element type allows us to have any type of object in a container (because of upcasting).
However, there is a price:
Integer
/ Circle
,
Square
(in our examples).
Object
and thus while in the array,
we can only use the methods of Object
.
Integer
variable, we can then call the methods of Integer
using that variable. Similarly, when we downcast from a Shape
to a Circle
, we can then call the methods of Circle
String
object to a container meant for Integer
, when we eventually retrieve the String
and downcast it to use it as an Integer
we will get a ClassCastException
Vector v = new Vector(); for (int i = 0; i < 10; i++) v.add(i); // all good, autboxing and upcasting to the rescue … v.add("dog"); // oops .. but no error, upcast againString
toObject
… // Now go to go through the vector and add 1 to each element for (int i = 0; i < v.size(); i++) v.set(i, ((Integer)v.get(i))+1); // downcasting as required
String
) will fail the downcast and throw a ClassCastException
class Vector<E> { @SuppressWarnings("unchecked") Vector() { this.capacity = INITIAL_CAPACITY; arr = (E [])new Object[capacity]; } E get(int index) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index " + index + " out of bounds for length " + size); return arr[index]; } void set(int index, E value) { if (index < 0 || index >= size) throw new IndexOutOfBoundsException("Index " + index + " out of bounds for length " + size); arr[index] = value; } void add(E value) { checkCapacity(); arr[size] = value; size++; } int getCapacity() {return capacity;} int size() {return size;} public String toString() { String result = "{"; for (int i = 0; i < size; i++) result += arr[i] + (i < size-1 ? ", " : ""); result += "}"; return result; } private void checkCapacity() { if (size < capacity) return; capacity *= 2; @SuppressWarnings("unchecked") E [] temp = (E [])new Object[capacity]; for (int i = 0; i < size; i++) temp[i] = arr[i]; arr = temp; } E [] arr; int capacity, size = 0; static final int INITIAL_CAPACITY = 10; }
T
(for type), or
E
(for element)m and occasionally a numeric suffix (e.g., T1
) if there are
more than one type (see the Pair
example below).
Object
, i.e., Object []
and then
cast it to the generic array, i.e., E []
@SuppressWarnings("unchecked")
checkCapacity
, or
in front of the surrounding method (as in the constructor).
public class VectorApp { public static void main(String [] args) { basics(); exceptions(); capacityManagement(); generics(); } static void basics() { System.out.println("=== Basic Functionality ==="); System.out.println("--- Creating Vector and printing it and its initial stats ---"); Vector<Integer> v = new Vector<>(); System.out.println(v + " (" + v.size() + "/" + v.getCapacity() + ")"); System.out.println(); System.out.println("--- Loading it up to capacity; add ---"); for (int i = 0; i < v.getCapacity(); i++) { v.add(i * 10); System.out.println(v + " (" + v.size() + "/" + v.getCapacity() + ")"); } System.out.println(); System.out.println("--- Incrementing each element; get and set ---"); for (int i = 0; i < v.size(); i++) v.set(i, v.get(i)+1); System.out.println(v); System.out.println(); } static void exceptions() { System.out.println("=== Exceptions ==="); Vector<String> v = new Vector<>(); try { System.out.println(v.get(0)); } catch (IndexOutOfBoundsException e) { System.out.println("Successfully caught exception on get: " + e); } try { v.set(0, ""); } catch (IndexOutOfBoundsException e) { System.out.println("Successfully caught exception on set: " + e); } System.out.println(); } static void capacityManagement() { System.out.println("=== Capacity Management ==="); Vector<Double> v = new Vector<>(); System.out.println("--- Creating Vector and loading it up with 1,000 elements ---"); int oldCapacity = -1; for (int i = 0; i < 1000; i++) { v.add((double)i); if (v.getCapacity() != oldCapacity) if (v.size() <= 10) System.out.println(v + " (" + v.size() + "/" + v.getCapacity() + ")"); else System.out.println("{" + v.get(0) + ", " + v.get(1) + "..." + v.get(v.size()-2) + ", " + v.get(v.size()-1) + "} (" + v.size() + "/" + v.getCapacity() + ") ... resized"); oldCapacity = v.getCapacity(); } System.out.println("{" + v.get(0) + ", " + v.get(1) + "..." + v.get(v.size()-2) + ", " + v.get(v.size()-1) + "} (" + v.size() + "/" + v.getCapacity() + ")"); } static void generics() { System.out.println("--- Generics"); System.out.println(); System.out.println("--- A Vector of doubles"); Vector<Double> vDouble = new Vector<>(); for (int i = 0; i < 10; i++) vDouble.add((double)i); System.out.println(vDouble); System.out.println(); System.out.println("--- A Vector of Strings"); Vector<String> vString = new Vector<>(); for (int i = 0; i < 7; i++) vString.add("Str" + i); System.out.println(vString); for (int i = 1; i < vString.size(); i++) System.out.println(vString.get(i).substring(1)); System.out.println(); System.out.println("--- A Vector of Vector<Integer>"); Vector<Vector<Integer>> vvInteger = new Vector<>(); for (int i = 0; i < 5; i++) { Vector<Integer> vInteger = new Vector<>(); for (int j = 0; j < i; j++) vInteger.add(j); vvInteger.add(vInteger); } System.out.println(vvInteger); } }
exceptions
AMD capacityManagement
methods were modified to
illustrate the generic facility, which is then expanded on in the newly introduced generics
method.
Integer
to String
and Double
0.0
to each element (we're not doing anything with them), but instead chose to
simply cast the index variable to double
double
autoboxing kicked in. But in Java, conversion of an int
to double
is legal, and implicit, so why couldn't we simply add the int
value to the
vector and let autoboxing do its magic there as well?
=== Basic Functionality === --- Creating Vector and printing it and its initial stats --- {} (0/10) --- Loading it up to capacity; add --- {0} (1/10) {0, 10} (2/10) {0, 10, 20} (3/10) {0, 10, 20, 30} (4/10) {0, 10, 20, 30, 40} (5/10) {0, 10, 20, 30, 40, 50} (6/10) {0, 10, 20, 30, 40, 50, 60} (7/10) {0, 10, 20, 30, 40, 50, 60, 70} (8/10) {0, 10, 20, 30, 40, 50, 60, 70, 80} (9/10) {0, 10, 20, 30, 40, 50, 60, 70, 80, 90} (10/10) --- Incrementing each element; get and set --- {1, 11, 21, 31, 41, 51, 61, 71, 81, 91} === Exceptions === Successfully caught exception on get: java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0 Successfully caught exception on set: java.lang.IndexOutOfBoundsException: Index 0 out of bounds for length 0 === Capacity Management === --- Creating Vector and loading it up with 1,000 elements --- {0.0} (1/10) {0.0, 1.0 ... 9.0, 10.0} (11/20) ... resized {0.0, 1.0 ... 19.0, 20.0} (21/40) ... resized {0.0, 1.0 ... 39.0, 40.0} (41/80) ... resized {0.0, 1.0 ... 79.0, 80.0} (81/160) ... resized {0.0, 1.0 ... 159.0, 160.0} (161/320) ... resized {0.0, 1.0 ... 319.0, 320.0} (321/640) ... resized {0.0, 1.0 ... 639.0, 640.0} (641/1280) ... resized {0.0, 1.0 ... 998.0, 999.0} (1000/1280) --- Generics --- A Vector of doubles {0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0} --- A Vector of Strings {Str0, Str1, Str2, Str3, Str4, Str5, Str6} tr1 tr2 tr3 tr4 tr5 tr6 --- A Vector of Vector{{}, {0}, {0, 1}, {0, 1, 2}, {0, 1, 2, 3}}
HashSet<Integer> hs = new HashSet<>();
HashSet hs = new HashSet();
class HomogeneousPair<T> { HomogeneousPair(T first, T second) { this.first = first; this.second = second; } void swap() { T t = first; first = second; second = t; } public String toString() {return "(" + first + ", " + second + ")";} T first, second; }
class HeterogeneousPair<T1, T2> { HeterogeneousPair(T1 first, T2 second) { this.first = first; this.second = second; } public String toString() {return "(" + first + ", " + second + ")";} T1 first; T2 second; }
swap
method?
class PairsDemo { public static void main(String [] args) { HomogeneousPair<Integer> homPair1 = new HomogeneousPair<>(1, 2); System.out.println(homPair1); homPair1.swap(); System.out.println(homPair1); System.out.println(); HomogeneousPair<String> homPair2 = new HomogeneousPair<>("dog", "cat"); System.out.println(homPair2); homPair2.swap(); System.out.println(homPair2); System.out.println(); HeterogeneousPair<Integer, String> hetPair2 = new HeterogeneousPair<>(1, "cat"); System.out.println(hetPair2); } }
(1, 2) (2, 1) (dog, cat) (cat, dog) (1, cat)