24 Java
24.1 Two Ideas: Java and Types
Types are a mechanism for enforcing data definitions and contracts.
Java is a programming language like the one we’ve seen, with a different syntax and with types.
24.2 Programming in Java
24.2.1 Java Syntax
;; A C is (C Number String String) (define-class C (fields x y z) ;; Number Number -> C (define (m p q) ...))
class C { |
Number x; |
String y; |
String z; |
public C m(Number p, Number q) { |
... |
} |
public C(Number x, String y, String z) { |
this.x = x; |
this.y = y; |
this.z = z; |
} |
} |
Let’s create a simple pair of numbers:
class Pair { |
Number left; |
Number right; |
|
public Pair(Number left, Number right) { |
this.left = left; |
this.right = right; |
} |
|
public Pair swap() { |
return new Pair(this.right, this.left); |
} |
} |
But really, this doesn’t work, because Java doesn’t have the type Number. So we’ll choose Integer instead.
class Pair { |
Integer left; |
Integer right; |
|
public Pair(Integer left, Integer right) { |
this.left = left; |
this.right = right; |
} |
|
public Pair swap() { |
return new Pair(this.right, this.left); |
} |
} |
To test this, we’ll import a testing library:
import tester.Tester; |
and write some examples:
class Examples { |
public Examples() {} |
public boolean testSwap(Tester t) { |
return t.checkExpect(new Pair(3,4).swap(), |
new Pair(4,3)); |
} |
} |
24.3 Running Java Programs
We don’t have a Run button in Java, so we need a different way to run our program. To do this, we first need to install our test library. This requires installing a JAR file from the NU Tester web site.
The we have to compile the program.
javac
The classpath and the -cp option
Now we have class files, which are binary and all mashed up
To run this, we use the java command, which also has a -cp option
If we change things, we have to recompile and then rerun.
24.4 A More Complex Example
What if we want to represent a union?
class Square { |
Integer size; |
public Square(Integer size) { |
this.size = size; |
} |
} |
class Circ { |
Integer radius; |
public Circ(Integer radius) { |
this.radius = radius; |
} |
} |
How do we declare that both of these are Shapes?
import tester.Tester; |
|
interface IShape {} |
|
class Square implements IShape { |
Integer size; |
public Square(Integer size) { |
this.size = size; |
} |
public IShape nothing(IShape i) { |
return i; |
} |
} |
|
class Circ implements IShape { |
Integer radius; |
public Circ(Integer radius) { |
this.radius = radius; |
} |
} |
|
class Examples { |
Examples () {} |
public boolean testNothing(Tester t) { |
Square s = new Square(5); // A local binding |
return t.checkExpect(s.nothing(new Circ(2)), |
new Circ(2)); |
} |
} |
|
24.5 Recursive Unions
import tester.Tester; |
class Mt implements IList { |
public Mt() {} |
} |
|
class Cons implements IList { |
Integer first; |
IList rest; |
|
public Cons(Integer first, IList rest) { |
this.first = first; |
this.rest = rest; |
} |
} |
|
interface IList {} |
|
class Examples { |
public Examples() {} |
|
public boolean testList(Tester t) { |
return t.checkExpect(new Mt(), new Mt()) |
&& t.checkExpect(new Cons(5, new Mt()), new Cons(5, new Mt())); |
} |
} |
|
24.6 Enumerations
In Fundies I, we might have written:
;; A Title is one of ;; - 'dr ;; - 'mr ;; - 'ms
In Java, we write:
interface ITitle {} |
class Dr implements ITitle { |
Dr() {} |
} |
class Mr implements ITitle { |
Mr() {} |
} |
class Ms implements ITitle { |
Ms() {} |
} |
Why write these silly constructors?
interface ITitle {} |
class Dr implements ITitle { |
Dr() {} |
} |
class Mr implements ITitle { |
Mr() {} |
} |
class Ms implements ITitle { |
Integer x; |
Ms() {} |
|
public Integer m() { |
return x+1; |
} |
} |
|
class Main { |
public static void main(String[] args) { |
new Ms().m(); |
return; |
} |
} |
Now we get a NullPointerException. But what is that?
A discussion of the evils of null.
For example, this compiles:
interface ITitle {} |
class Dr implements ITitle { |
Dr() {} |
} |
class Mr implements ITitle { |
Mr() {} |
} |
class Ms implements ITitle { |
Integer x; |
Ms(Integer x) { |
this.x = x; |
} |
|
public Integer m() { |
return null; |
} |
} |
|
class Main { |
public static void main(String[] args) { |
new Ms().m(); |
return; |
} |
} |
Never write null in your program!
A long sermon on null.
24.7 Parameterized Data Definitions
Consider our Pair class:
class Pair { |
Integer left; |
Integer right; |
|
public Pair(Integer left, Integer right) { |
this.left = left; |
this.right = right; |
} |
|
public Pair swap() { |
return new Pair(this.right, this.left); |
} |
} |
Now if we want a Pair of Strings:
class PairString { |
String left; |
String right; |
|
public Pair(String left, String right) { |
this.left = left; |
this.right = right; |
} |
|
public Pair swap() { |
return new Pair(this.right, this.left); |
} |
} |
This is obviously bad—
class Pair<T,V> { |
T left; |
V right; |
|
public Pair(T left, V right) { |
this.left = left; |
this.right = right; |
} |
|
public Pair<V,T> swap() { |
return new Pair<V,T>(this.right, this.left); |
} |
} |
|
class Examples { |
public Examples() {} |
public boolean testSwap(Tester t) { |
return t.checkExpect(new Pair<Integer,Integer>(3,4).swap(), |
new Pair<Integer,Integer>(4,3)); |
} |
} |
|
24.8 Abstraction
|
class C { |
Integer x; |
Integer y; |
C(Integer x, Integer y) { |
this.x = x; |
this.y = y; |
} |
|
public Integer sq() { |
return this.x * this.x; |
} |
} |
|
|
class D { |
Integer x; |
String z; |
D(Integer x, String z) { |
this.x = x; |
this.z = z; |
} |
|
public Integer sq() { |
return this.x * this.x; |
} |
} |
|
Now to abstract:
|
class S { |
Integer x; |
public Integer sq() { |
return this.x * this.x; |
} |
S(Integer x) { |
this.x = x; |
} |
} |
|
class C extends S { |
Integer y; |
C(Integer x, Integer y) { |
super(x); |
this.y = y; |
} |
} |
|
|
class D { |
Integer x; |
String z; |
D(Integer x, String z) { |
this.x = x; |
this.z = z; |
} |
|
public Integer sq() { |
return this.x * this.x; |
} |
} |
|