Before we jump into Object Oriented Programming ...
Before we jump into Object Oriented Programming, we will round out
our introduction to the structure of Java programs by consdering
how we would write the kind of procedural programs we're used to
in the Java lanaguage / run-time environment. In theory this is
something you should not do! You should use OOP with Java.
However, seeing how the pieces fit together in a familiar kind of
program will be valuable, and put you in the right place from
which to begin our investigation of OOP.
-
classes as collections of functions (collections of methods in
Java parlance)
-
calling methods/functions of one class from within another
-
classes as collections of data, i.e. classes as structs
-
understanding how Java programs involving multiple class files run
A problem to motivate us
Suppose we wanted to write a program that allowed the users to play
the card
game
Blindman's bluff poker.
The idea is that each player gets a card, but only gets to see
their opponent's card, not their own. A bet is placed by one
player, then the next player raises, calls or folds. In case of a
raise, the first player can either call or fold. "Call" means
match the bet, "fold" means give up (other player gets money), and
"raise" means match the bet and then add some more. Let's say
there's a 10cent ante (initial amount put in before the game
starts) and a 50cent maximum bet. In our game, all players
start with $5. The game ends when all but one player is out of
money, or at any point the users want to stop. We might not
finish the game in class, but the point is to see how our
procedural programming ideas in C++ map to Java (although,
ultimately, in Java we will want to use object oriented
programming, not procedural programming
Representing a cards:
We're going to use the same representation of a card as an int
that we did in IC210, summarized below:
- represent suits by number according to these rules:
0 = ♣, 1 = ♦, 2 = ♥, 3 = ♠
-
represent face values according to these rules:
2 = 2, 3 = 3, ..., 10 = 10, 11 = J(jack), 12 = Q(queen),
13 = K(king), 14 = A(ace)
-
represent a card as a number by the rule:
cardvalue = 100 × suit + facevalue
and (suit = cardvalue/100, facevalue=cardvalue%100)
So, for example, the card
Q♥ → (suit=2,facevalue=12) → 100×2 + 12
= 212
In the other direction,
cardnumber 314 = 100×3 + 14 (i.e. 314/100 = 3,
314%100 = 14) →
(suit=3,facevalue=14) → A♠
Our Unix terminal program understands
"UTF-8" character encodings, which means we can
print card suits to the screen.
Here are the suits and the corresponding string for
printing them in unicode.
♣ ← "\u2663",
♦ ← "\u2666",
♥ ← "\u2665",
♠ ← "\u2660".
Step 1: will be to write a program that reads
in a number N followed by N player names,
deals each player a card from a shuffled deck, and
prints out the N
players in order with their, names, amount of money and card.
For example:
3 Joe Sue Pat
Joe $5 10♥
Sue $5 K♦
Pat $5 3♠
Note: Of course the point is to do this in such a way that
we could easily extend it to play more and more of the real game ...
A C++ program for Step 1 (in a Procedural Programming Style!)
Let's start off with a C++ solution for Step 1, as you might write
it at the end of IC210/SI204.
Note 1: There are no comments here so I can keep
everything to one page, and because we will discuss the code a bit
in class.
Note 2: There is a memory leak in this code,
can you spot it?
Note 3: The .h files should have #ifndef's
protecting against multiple inclusions ...
Important disclaimer ...
DISCLAIMER!
We will keep the procedural
style, which is not how you are supposed to program in Java!
So a few weeks from now, you would do this in a totally
different way. You would write it in
an object-oriented style, which is what we are supposed
to be doing in Java.
Translating card.h/card.cpp into Java
In-class exercise: Your instructor will give you printouts
of the C++ code, and ask you to break up into groups of two or
three and find all the things that will have to change to translate
card.h/card.cpp into Java.
Then as a class we will go over the complete translation, which
will give us something like ...
|
-
The .h file isn't needed, because in Java we don't
separate the declaration of a function/method's
prototype from its definition. They are the same.
However, and this is important, the notion of a
function's prototype is very much there, except that in
Java it is referred to as the function's
signature.
-
All the function definitions need to be wrapped up in a
public class definition. It might seem natural to name the
class "
card", but according to Java's
naming conventions we really ought to call it
"Card", because class names should begin
with uppercase letters.
-
The source code file is named
Card.java
because the file name must match the name of the public
class inside.
-
Each function definition must be prefaced with
"
public static".
-
The function names "cardstring" and "mkdeck" need to be
changed to "cardString" and "mkDeck" to match camel-case
conventions.
-
Array declaration
string* F = new string[15]{ ...
needs to be changed to
String[] f = new string[]{ ...
to match Java's array declaration rules and naming
conventions. Needing to remove the "15" from the "[ ]"
is interesting! It is a compile-time error to both give
a size for the array and give an initializer list of
values for the array. This makes some sense, since the
array length can be inferred from the initializer list.
We also make the analogous changes for the "S" array and
the "D" array.
-
Important!
Creating the f and s arrays in cardString does not cause
a memory leak in Java as it did in C++. This is because
Java is "garbage collected", so the f and s arrays will
be automatically reclaimed when the JVM recognizes they
are no longer needed.
-
The
shuffle function/method changes the
most. This is because there is no "global"
function rand that we can call to generate
a random number. Instead, we need a Random
object that we use to call the nextInt
method. (This will make more sense once we get a bit more
into OOP.)
So, the prototype (or "signature" in Java parlance)
for the shuffle function has to change
to add a new parameter Random rand> so we
can make the call "rand.nextInt(52)".
-
Last, but not least, each public class is allowed to
have its own
public static void main(String[] args)
method. This is a huge benefit when we try "bottom-up"
programming, because it makes it easy to add in testing
code - in this case that means code that is there to
test the function/methods we have put into class Card,
but which is not part of the larger program we are
ultimately creating. We've made use of exactly that
feature here:
$ java Card
Testing Card ...
Orig Deck: 2♣ 3♣ 4♣ 5♣ 6♣ 7♣ 8♣ 9♣ 10♣ J♣ K♣ Q♣ A♣ 2♦ 3♦ 4♦ 5♦ 6♦ 7♦ [...]
Shuffled : 6♠ 5♦ 8♣ 10♦ J♠ 4♥ 2♣ 9♠ 2♠ K♦ A♠ Q♣ K♥ 6♦ 6♥ 7♠ 3♥ 2♦ 5♣ 9♦ [...]
$ java Card
Testing Card ...
Orig Deck: 2♣ 3♣ 4♣ 5♣ 6♣ 7♣ 8♣ 9♣ 10♣ J♣ K♣ Q♣ A♣ 2♦ 3♦ 4♦ 5♦ 6♦ 7♦ [...]
Shuffled : 10♠ 8♥ 5♣ 5♥ 5♦ 9♦ 9♣ Q♠ 6♦ 7♠ A♠ K♠ K♥ 7♦ A♥ J♥ 10♥ 2♠ 6♣ [...]
|
Calling Card functions from another file
We will be translating the C++ file
game.cpp into a
Java file
Game.java. However, to do that we need to
know how to call Card functions/methods from within another
class. The answer is simple: the full name of
public static method
foo defined within
class
Bar is
Bar.foo. Within class
Bar we don't need the full name, outside the class we
need the full name. For example:
Structs in Java
The Java version of C++ struct is a class definition
that contains data rather than functions/methods.
Important! these data elements must be
declared
public but not
static!
[We will discuss "public" and "static" thoroughly in a few
classes, at which point these will not be arbitrary rules. But
for now, you just have to accept them as arbitrary rules. ]
You create instances of these struct-like objects using new, like
this:
TypeName x = new TypeName();
... and you access fields using a ".", like this:
System.out.println(x.fieldName);
So we might make a struct-like class Player like
this:
|
|
$ javac Player.java Rat.java
$ java Rat
Jose
$5.0
114 |
|
|
Translating game.cpp into Java
Finally we translate
game.cpp into Java.
We will have public class
Game, which will have its
own public static main function. And the magic of Java is that
the main functions of separate classes can co-exist within the
same program!
All together now
It's helpful to see all three pieces together all at once. So
here's what that looks like:
It may be helpful to see the original C++ and the Java versions
side by side: