Lecture 8: Union, Interfaces, and Lists in Java
An IDE for Java
We’re going to be using the IntelliJ integrated development environment (IDE) to develop and run Java programs. Like DrRacket, it will provide useful features to help us in the process of developing programs.
We’ll do a short demo in class and in lab.
Unions in Java: Traffic Lights
Let’s now turn back to modelling information in Java. So far we’ve seen how to represent compound data such as coordinates. Another common kind of data definition is a union data definition. Let’s look at a very simple union data definition (that we first looked at in class/0: traffic lights.
We define the union of objects in terms of their interface; the method signatures that each variant of the union has in common. In class/0 this takes the form:
;; A Light implements ;; ;; Compute the next traffic light after this one ;; next : -> Light
interface Light { // Compute the next traffic light after this one Light onTick(); }
In class/0, we would define a class and declare it implements the interface in a comment. In Java, we can declare the intention to implement the interface as part of the class definition:
class Red implements Light { Red() {} }
Nullary Constructor by Default. Note the Red constructor takes no arguments and does nothing, which is what we want since there are no fields in the Red class. As a side note: we can omit this nullary constructor, since it is the default constructor Java will use if no constructor is defined.
class Red implements Light {}
Now, this class says that it implements the Light interface, but so far it has not actually defined the next method as required by the Light interface.
The Java type system will catch this error for us. If you tried to compile this program as-is, there would be a compile-time error:
Error: Red is not abstract and does not override abstract method next() in Light.
In the IDE, this static error is often indicated with a red squiggly underlining of the class definition.
Let’s make a stub for next method:
class Red implements Light { // Compute the next traffic light after this red light Light next() { return this; } }
class Red implements Light { // Compute the next traffic light after this red light public Light next() { return this; } }
This public keyword is the visibility modifier. Using it here indicates that next can be called from outside of the Red class, which is (implicitly) what’s required by the interface. The are also visibility modifiers private and protected. We will mostly ignore visibility issues for now and assume that methods are public and fields are private. We will add modifiers when needed to make programs type-check. For more details, see the Oracle tutorial.
class Red implements Light { // Compute the next traffic light after this red light public Light next() { return this; } } class Yellow implements Light { // Compute the next traffic light after this yellow light public Light next() { return this; } } class Green implements Light { // Compute the next traffic light after this green light public Light next() { return this; } }
Thinking through some examples, it’s easy to come up with the correct code for the method definitions:
class Red implements Light { // Compute the next traffic light after this red light public Light next() { return new Green(); } } class Yellow implements Light { // Compute the next traffic light after this yellow light public Light next() { return new Red(); } } class Green implements Light { // Compute the next traffic light after this green light public Light next() { return new Yellow(); } }
So now we’ve seen our first union data definition in Java.
Grammar: Java with Interfaces
The grammar of Java programs has grown some now. In particular a program now consists of some number of class or interface definitions. Since a set of objects may be defined as either a class or interface, we use ‹type-name› to range over things defined as classes or interfaces. The grammar is also updated to have optional visibility modifiers on method definitions. Here are the revised parts of the grammar (only):
| ‹program› | ::= | ‹class-or-intf-defn›* |
| ‹class-or-intf-defn› | ::= | ‹class-defn› |
|
| | | ‹intf-defn› |
| ‹intf-defn› | ::= | interface ‹int-name› { ‹method-sigs› } |
| ‹field-decl› | ::= | ‹type-name› ‹field-name› ; |
| ‹method-sigs› | ::= | |
|
| | | ‹method-sig› ; ‹method-sigs› |
| ‹method-sig› | ::= | ‹type-name› ‹meth-name› ( ‹param-list› ) |
| ‹method-defn› | ::= | [‹vis-modifier›] ‹method-sig› { return ‹expr› ; } |
| ‹vis-modifier› | ::= | public |
|
| | | private |
| ‹type-name› | ::= | ‹class-name› |
|
| | | ‹intf-name› |
| ‹intf-name› | ::= | ‹id› |
Recursive Unions in Java: Lists
TBD