Learn Programming in Java


<<Previous | ToC | Next >>

Object-Oriented Programming (OOPS)

Java is an OOPS language, so most people who teach Java begin with OOPS. I'm here to teach you programming, and OOPS only to the extent that it helps you write Java. There is a section on OOPS in my "Things You Need to Know in Java" which is different from what this page tells you -- perhaps you will eventually want to read both -- but today you will learn enough about OOPS to write a game using my GameEngine. After you are comfortable writing games in Java (including within the GameEngine structure), you can Google "object oriented programming" and learn what I didn't tell you, but it isn't much and it won't make you a better programmer (just more knowledgeable).
 

Why?

You might be thinking, "If Tom is so down on OOPS, why teach Java?" Maybe "down on" is too strong, I think OOPS is a wonderful way to get certain kinds of type-safety that simply are not available any other way (see my explanation in "Things You Need to Know"), it's just that the promoters of OOPS have oversold their product. There is a lot of junk in OOPS (and in Java, even increasing) that should not be in a production language. Officially Java does not compile to machine language (although most implementations support a Just-in-Time ("JIT") native code recompiler that is almost as good as direct machine language), but this fact makes Java unusable for systems software, basically it lacks the generality of C.

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.
 

OOPS Is Subroutines

OOPS is Subroutines writ large, nothing more or less. A subroutine is a collection of code and local data with a name, so it can be used to do a specific task from (almost) anywhere else in the program, without the programmer needing to think about how it is done. A class is a collection of code and local data with a name, so it can be used to do a specific task from (almost) anywhere else in the program, without the programmer needing to think about how it is done. Classes can (and usually do) also have included subroutines (called "methods" but they are just subroutines). In some older languages (not C, of which Java is a derivative) subroutines can also have included subroutines. Class variables do not vanish between invocations like subroutine data, but in some languages (C is one of them) the subroutines can keep their own private non-volatile data.

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;
System.out.println(myPop.MyTitle() + "...");
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
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 that 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.
 

Objects In GameEngine

This is now where the rubber hits the road. GameEngine specifies two classes that you must be concerned with. Actually three, but the other one is only a name, you don't actually do anything OOPSy with it, except that when you need to call a method you use the name.

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: 2021 June 2a