class SSNum { … public int getArea() {…} public int getGroup() {…} public int getSerial() {…} public String toString() {…} … }
String
, complete with the separating '-'s.
toString
.
class SSNum { SSNum(String ssnum) {this.ssnum = ssnum;} public int getArea() {return Integer.parseInt(ssnum.substring(0, 3));} public int getGroup() {return Integer.parseInt(ssnum.substring(4, 6));} public int getSerial() {return Integer.parseInt(ssnum.substring(7));} public String toString() {return ssnum;} private String ssnum; }
Integer.valueOf
is an acceptable alternative to parseInt
Integer
, the latter an int
, but with autoboxing, the
result is basically the same (at least here)
class SSNumApp { public static void main(String [] args) { SSNum sSNum = new SSNum("123-45-6789"); System.out.println(sSNum); System.out.println(sSNum.getArea()); System.out.println(sSNum.getGroup()); System.out.println(sSNum.getSerial()); } }
int
(e.g., it takes up less space than the 11-byte string).
class SSNum { SSNum(String ssnum) { this.ssnum = Integer.parseInt(ssnum.substring(0, 3)) * 1000000 + Integer.parseInt(ssnum.substring(4, 6)) * 10000 + Integer.parseInt(ssnum.substring(7));} public int getArea() {return ssnum / 1000000;} public int getGroup() {return (ssnum / 10000) % 100;} public int getSerial() {return ssnum % 10000;} public String toString() {return String.format("%03d-%02d-%04d", getArea(), getGroup(), getSerial());} private int ssnum; }
toString
toString
has to transform the internal representation into a String
toString
which takes the internal representation and transforms it into
a string suitable for human viewing.
StringSSNum
. When we shift to an int
-based
implementation, that name would no longer be appropriate.
StringSSNum
, the app would read:
StringSSNum sSNum = new StringSSNum("123-45-6789");would have to be changed to:
IntSSNum sSNum = new IntSSNum("123-45-6789");(note that I've left the variable name the same 'vanilla' name (
ssNum
); changing it from stringSSNum
to intSSNum
just introduces even more name changing.)
convertToInt
method in our StringSSNum
implementation:
int convertToInt() {…}
StringSSNum
may have thought it a good idea to have such a method; and
furthermore, made heavy use of this method
IntSSNum
) — which justifiably would not have
such a method, the application code would break wherever that method was called.
IntSSNum
) but that then involves making changes
to our tested class.
asInt
would be less
egregious, and could be argued to be appropriate under all implementations.
Java's interface
construct allows us to specify a set of behavior in the form of method specifications.
The interface forms a data type, i.e., a specification of a set of values and the valid operations
on those values. Any class that implements
the interface is said to belong to the data type of the interface,
i.e., an object of the implementation class is-a
object of the interface type as well, and can appear in any context
interface reference variables appear.
interface SSNum { int getArea(); int getGroup(); int getSerial(); }
toString
is typically not specified in the interface
toString
method from Object
so the method has already been specified and a (default) implementation is already provided
class StringSSNum implements SSNum {
StringSSNum(String ssnum) {this.ssnum = ssnum;}
public int getArea() {return Integer.parseInt(ssnum.substring(0, 3));}
public int getGroup() {return Integer.parseInt(ssnum.substring(4, 6));}
public int getSerial() {return Integer.parseInt(ssnum.substring(7));}
public String toString() {return ssnum;}
private String ssnum;
}
class IntSSNum implements SSNum {
IntSSNum(String ssnum) {this.ssnum = Integer.parseInt(ssnum.substring(0, 3)) * 1000000 +
Integer.parseInt(ssnum.substring(4, 6)) * 10000 + Integer.parseInt(ssnum.substring(7));}
public int getArea() {return ssnum / 1000000;}
public int getGroup() {return (ssnum / 10000) % 100;}
public int getSerial() {return ssnum % 10000;}
public String toString() {return String.format("%03d-%02d-%04d", getArea(), getGroup(), getSerial());}
private int ssnum;
}
class StringSSNumApp { public static void main(String [] args) { StringSSNum stringSSNum = new StringSSNum("123-45-6789"); System.out.println(stringSSNum); System.out.println(stringSSNum.getArea()); System.out.println(stringSSNum.getGroup()); System.out.println(stringSSNum.getSerial()); } }
class IntSSNumApp { public static void main(String [] args) { IntSSNum intSSNum = new IntSSNum("123-45-6789"); System.out.println(intSSNum); System.out.println(intSSNum.getArea()); System.out.println(intSSNum.getGroup()); System.out.println(intSSNum.getSerial()); } }
StringSSNum
vs IntSSNum
). This is used in
IntSSNum intSSNum
).
new StringSSNum(…);
).
stringSSNum
vs intSSNum
)
ssNum
in both apps.
ssNum
.
IntSSNum
one must write new IntSSNum
,
(clearly one wouldn't write new StringSSNum
and you can't create an SSNum
(it's an interface, not a concrete class).
newIntSSNum
or new String SSNum
), we pass the object in as a parameter,
allowing the caller to be responsible for which to use.
SSNum
by virtue of their
having implemented the SSNum
interface.
SSNum
app, we are also making sure we are only using those methods
of the interface, i.e., we are indeed 'programming to the interface'.
class SSNumApp { public static void demo(SSNum sSNum) { System.out.println(sSNum); System.out.println(sSNum.getArea()); System.out.println(sSNum.getGroup()); System.out.println(sSNum.getSerial()); } }
class StringSSNumApp { public static void main(String [] args) { SSNumApp.demo(new StringSSNum("123-45-6789")); } }
class IntSSNumApp { public static void main(String [] args) { SSNumApp.demo(new IntSSNum("123-45-6789")); } }
In other words, the ADT is the abstraction of a type, while the data structure is the implementation representation of the type.
interface
construct acts as a mechanism for the specification of an ADT. Taking our Vector
as an example, we could abstract our notion of a vector int the following interface:
interface Vector<E> { E get(int index); void set(int index, E value); void add(E value); int getCapacity(); int size(); }
class ArrayVectorimplements Vector { // Our Version 5Vector
class Vector() {…} E get(int index) {…} void set(int index, E value) {…} void add(E value) {…} int getCapacity() {…}return capacity;} int size() {…}return size;} public String toString() {…} private void checkCapacity() {…} E [] arr; int capacity, size = 0; static final int INITIAL_CAPACITY = 10; }
Vector
we need a new name for our concrete class
ArrayVector
.
implements Vector
),
using those variables
toString
: not specified in the interface as it's already inherited from Object
and overridden in this class
getCapacity
: as we'll see shortly capacity is part of the implementation, i,e. typically
not seen by the user. We've placed it in the set of public methods until now for the purposes
of illustrating the capacity-related behavior of the class, but strictly speaking it;s not part of the interface,
and I've left it out of the current version
checkCapacity
: regardless of whether we want to display capacity info and thus add checkCapacity
should definitely be private
List
, ArrayList
, and LinkedList
Vector
class.
List
ArrayList
and
LinkedList
ArrayList
uses and array as the underlying implementation container, while LinkedList
uses something called a linked represntation
List
interface, i.e., we
will program to the interface
LinkedList
class without its details; all we are concerned with is the
fact that it implements the List
interface.
import java.util.List; import java.util.ArrayList; public class ListApp { static void doIt(List<Integer> basicList, List<String> exceptionList, List<Double> listDouble, List<String> listString) { System.out.println("===== ListApp ====="); basics(basicList); exceptions(exceptionList); generics(listDouble, listString); } static void basics(List<Integer> list) { System.out.println("=== Basic Functionality ==="); System.out.println("--- Printing and initial stats ---"); System.out.println(list + " (" + list.size() + ")"); System.out.println(); System.out.println("--- Loading it up to capacity; add ---"); for (int i = 0; i < 10; i++) { list.add(i * 10); System.out.println(list + " (" + list.size() + ")"); } System.out.println(); System.out.println("--- Incrementing each element; get and set ---"); for (int i = 0; i < list.size(); i++) list.set(i, list.get(i)+1); System.out.println(list); System.out.println(); } static void exceptions(List<String> list) { System.out.println("=== Exceptions ==="); try { System.out.println(list.get(0)); } catch (IndexOutOfBoundsException e) { System.out.println("Successfully caught exception on get: " + e); } try { list.set(0, ""); } catch (IndexOutOfBoundsException e) { System.out.println("Successfully caught exception on set: " + e); } System.out.println(); } static void generics(List<Double> listDouble, List<String> listString) { System.out.println("--- Generics"); System.out.println(); System.out.println("--- A List of doubles"); for (int i = 0; i < 10; i++) listDouble.add((double)i); System.out.println(listDouble); System.out.println(); System.out.println("--- A List of Strings"); for (int i = 0; i < 7; i++) listString.add("Str" + i); System.out.println(listString); for (int i = 1; i < listString.size(); i++) System.out.println(listString.get(i).substring(1)); System.out.println(); } }
IntSSNum
and StringSSNum
created an instance of themselves in their own app,
and passed it on to the (common) interface-based app (SSNumApp
).
List
objects and send them along to the above app. This does lead to a bit of a mess in the parameter lists of the app.
VectorApp
has been replaced with
a load of 10 elements (no particular reason, and although I should have declared a constant for that value and used it,
it would have been more of a distraction than simply stating 'putting 10 elements into the list').
System.out.println("--- A List of ListIn keeping with the whole notion of injection, we don't know what type of list to create"); ???<???<Integer>> ??Integer = new ???<>(); for (int i = 0; i < 5; i++) { ???List<Integer> ???Integer = new ???<>(); for (int j = 0; j < i; j++) ???Integer.add(j); listListInteger.add(???Integer); }
Vector
.
But now, there are at least to implementations of List
(ArrayList
and
LinkedList
and furthermore, in the above app, we are in the context of simply a List
,
the question becomes what sort of List
do we create for the elements of our List<List<Integer>>
import java.util.List; import java.util.ArrayList; public class ArrayListApp { public static void main(String [] args) { ListApp.doIt(new ArrayList<Integer>(), new ArrayList<String>(), new ArrayList<Double>(), new ArrayList<String>()); } }
import java.util.List; import java.util.LinkedList; public class LinkedListApp { public static void main(String [] args) { ListApp.doIt(new LinkedList<Integer>(), new LinkedList<String>(), new LinkedList<Double>(), new LinkedList<String>()); } }