<<Previous | ToC
| Next >>
RoadMap: Today you begin the final segment in our programming regimen, learning about Object-Oriented Programming (OOPS) so that you can do real graphics on some of the same programs you already understand how they work, but now also give the user more control. When you finish you will be ready to write anything you want. You are almost there. Stay with us.
This course is about Java because Java is the best programming language out there today with enough users to keep it alive and well. As I write this, SCOTUS (the Supreme Court Of The US) recently released their decision on a far-reaching case between Google and Oracle. Nobody seems to have realized that if they had decided for Oracle, it would have destroyed Java -- the 800-pound gorilla in this story is Google, and they would have switched Android to some proprietary language, and that would have been the end of Java -- but SCOTUS did The Right Thing and we still have Java for the forseeable future: like Fortran, there will be a language called Java 50 years from now; it may not look like the language you are learning, but it will be called "Java" (unless Oracle does something else as stupid as suing Google). There are other languages out there with more users, but they are either untyped, or so weakly typed as to be useless. If you must use one of these other (dangerous) languages -- and the Real World is programmed in C, so that is a likely prospect -- think in Java while writing in C or Python, and your code will have fewer bugs and probably run faster. But you will think in Java only if Java is your "mother tongue" (the language you learned first).
That's why I teach Java.
One important difference is that classes are data types, so you can have variables that refer to this particular collection of subroutines and data. That is useful because classes can be subclassed so that variables defined to be of the type specified by the superclass (the parent class) can be assigned values that are references to a subclass, which -- and this is the important part -- the subclass can rewrite the subroutines, and calls to a subroutine in the superclass will actually call the corresponding subroutine in the subclass, if that is what the variable refers to.
This is easier to understand with a specific example. Let's define a
class Person {
private String name;
private int age;public String WhoAmI() {
return name;}public String DoWhat() {
if (age<18) return "student";
return "?";}public Person(String nm, int old) { // constructor
name = nm;
age = old;}
} // end class Person
Then somewhere else in the program we can make variables of type
Person:
Person Meg = new Person("Margaret",12);
Person Dad = new Person("John Smith",40);
Then somewhere else, we might print out
System.out.println(Meg.WhoAmI() + " is a " + Meg.DoWhat() + ".");we get
Margaret is a student.
That's all fine and dandy, but the subclass makes it more interesting:
class Teacher extends Person {
// private String name; // these are already included
// private int age;
private int degree;
private String domain;public String MyTitle() {
if (degree>20) return "Dr.";
if (degree>16) return "Mr.";
return "";}public String WhoAmI() {
return MyTitle()+super.WhoAmI();}public String DoWhat() {
return "teacher";}public Teacher(String nm, int old, String major, int edu) { // const'r
super(nm,old); // base class constructor makes the assignment
domain = major;
degree = edu;}
} // end class Teacher
Then we can change the creation of variable Dad
Person Dad = new Teacher("John Smith",40,"history",22);
Dad is still a Person, but he is now also a Teacher.
So when we print out
System.out.println(Dad.WhoAmI() + " is a " + Dad.DoWhat() + ".");we get
Dr.John Smith is a teacher.
The WhoAmI method in the subclass Teacher is said
to override the same method in the base class Person.,
and even though the program that is printing out who John Smith is only
knows he is a Person, the underlying OOPS
mechanism knows he is a Teacher and gets the methods from the
Teacher
class.
Now suppose we have variable Dad which the compiler knows is a Person. Somewhere else in the program we want to introduce him with his proper title, but the compiler won't let us say Dad.MyTitle() because Person does not have a MyTitle() method. You and I, we know that he is also a Teacher, so we can make a new variable
Teacher myPop = (Teacher)Dad;and at runtime Dad is cast to a Teacher., which works because Dad is also a Teacher. But if somewhere between there and here, we had (perhaps as a joke, more likely as a mistake) written
System.out.println(myPop.MyTitle() + "...");
Dad = Meg;then the compiler won't complain, because both Dad and Meg are type Person. But then you run the previous line,
myPop = (Teacher)Dad;the compiler might warn you that this could be an error, and when you run it, Kaboom! You get a ClassCastException. Your program stops. Mostly these compiler errors and runtime exceptions are your friends, treat them nicely and you will be much more productive than the people using untyped languages.
Within a class method there is an implied object this which
is whatever object was used to call this method. You can call other methods
using the same object as (for example) this.DoWhat() or whatever,
or you can simply omit the reference and call DoWhat() by itself
with the same effect.
Like Calculator, your game will be its own class, but it will be a subclass of the existing class GameEvent, which I defined as part of the GameEngine. That means all the methods I defined for GameEvent will already be methods of your game. So when the GameEngine wants to tell your game that the user clicked the mouse on one of the artifacts on the screen of your game, it will call the predefined method ClickEvt, and your game will know exactly what got clicked, and your code can respond to that event appropriately. OOPS does that for us. We could do it before, but it was really hard to do it safely unless everything was compiled together. Separate game engines (like mine) that outsiders (like you) could write games for did not, and could not, exist.
The other predefined class you will use a lot in your GameEngine game is GameWgt for specifying the visible artifacts ("widgets") the user sees on your game board (the game board itself is also a widget). There are several (internal) subclasses, but you do not need to think about them, only the base class GameWgt. Like Person in our example above, all the methods in the subclasses are available to the caller who needs only to think of it as a GameWgt. Mostly you won't even directly create widgets, you will define them in a tool I call "GameMaker" (which is also a subclass of GameEvent, running in the GameEngine) and the GameMaker will build a stub version of your game class, so that all you need to do is add (Java) code to make the game play. The widgets will already be there and the right color, and if you say so, they will already be moving, but your code decides what to do when they bump into something, or the user clicks somewhere, and so on. You will have given the interesting widgets names in GameMaker, and your code can access those widgets by name to move them around or change color or whatever. OOPS does that for us.
Programming the GameEngine involves a different way of thinking about your program. I call it "event-driven" (in the GameEngine) compared to "flat-file" (the way all your previous programs and games were). It's the difference between programming for people or programming for machines. People are not machines, but sometimes the machines don't know that. You can be better.
Next: Events
<<Previous | ToC | Next >>
Revised: 2023 February 1