Forging The Rings!

From Huben's Wiki
Jump to: navigation, search

Today we will study with Celebrimbor, forger of the rings of power.

Contents

The Ring Class

We must create a class Ring that has two String fields for each ring: the metal and who the ring is givenTo: elves, men, dwarves, or the dark lord Sauron. You will need accessor and modifier methods for the private givenTo field because the rings can change hands (to "hobbit" for example) and can be concealed. We will also want a private class (or static) field for the number of rings forged called numberForged and an accessor only for it. No other class should be able to change the recorded number of rings forged, but it should be made visible.

class Ring
{
  private String givenTo;
  public final String metal;
  private static int numberForged = 0;
...
}

The metal field must have some special properties. Everyone should be able to see what metal a ring is made of, but they must not be allowed to change it. A ring's metal can never change: it must be immutable. The way to do that is to use the keywords public and final for the type of the field. But how can we set a final field? You shall witness the subtlety of the master smith Celebrimbor after you create the Ring class with its three fields and its three methods.

Forging A Ring

When we will create a Ring instance, we use a constructor. For example, Ring oneRing = new Ring(); Until now, we have been using constructors to make objects without really understanding what constructor methods do and their true power. Behold a constructor for Ring, which we will study at length.

public Ring(String theMetal, String whom)
{
  metal = theMetal;   // metal can only be set once, in a constructor, because it is final
  givenTo = whom;   // givenTo can be changed later: it is an ordinary
  numberForged++;  // add one to the number forged
}

Constructors such as this one are methods with arguments. This constructor could be called with: Ring oneRing = new Ring("gold", "Sauron"); The arguments are variables. Their values are set when the constructor is called (right after a new keyword.) theMetal is set to "gold" and whom is set to "Sauron".

Setting the metal and givenTo fields with the values of the arguments is obvious. The metal field will never be able to be set again for this instance, because it is final. But the subtle thing is that the class variable numberForged, which started at zero, is now increased by one every time this constructor is called, which means for every ring instance created. We are counting the number of rings created!

  • Add a toString() method to write: "I am a X ring, made for Y." X should be the metal field, Y should be the givenTo field.
  • Add a static forged() method that returns the String "There have been N rings forged." N should be numberForged.

Let us now show how this is used to forge rings!

Building The Forge

class Forge
{
  static public void main (String[] args)
  {
    System.out.println(Ring.forged());
    Ring oneRing = new Ring("gold", "Sauron");
    System.out.println(Ring.forged());
    Ring[] elvenRing = new Ring[3];  // Array of 3 rings for elves.
    System.out.println(Ring.forged());
    System.out.println(elvenRing[0]);
    elvenRing[0] = new Ring("silver", "an elf");
    elvenRing[1] = new Ring("silver", "an elf");
    elvenRing[2] = new Ring("silver", "an elf");
    System.out.println(oneRing);
    System.out.println(elvenRing[0]);
    System.out.println(elvenRing[1].toString());
    System.out.println(elvenRing[2]);
    System.out.println(Ring.forged());
  }
}

Let's understand what's happening in Forge.main(). Forge.main() is a static method. Without an instance variable of something, it can only call other static methods: such as Ring.forged().

  • The first time we call Ring.forged(), it shows that 0 have been forged.
  • The oneRing instance is created by calling the constructor and setting the constructor argument theMetal to "gold" and whom to "Sauron".
  • Now when we call Ring.forged(), it shows that 1 ring has been forged.
  • Ring[] elvenRing = new Ring[3]; creates an array of three Ring ***VARIABLES***. It does not create any Ring instances for those variables to point to. Note the peculiar use of the square brackets instead of parentheses: no constructor is being called!
  • Ring.forged() shows that we have not created any new instances.
  • So what is in those three Ring variables, if they are not pointing to Ring instances? The value null. When we try to println() one of the Ring variables, elvenRing[0], the result is a null.
  • We laboriously create three Ring instances and assign them to each Ring variable in the array.
  • Now we can print out all the rings created, and how many were forged.
  • Notice that your toString() method is used explicitly to print elvenRing[1]. Then println() prints the string value that is returned. But you obviously don't need to do that: we don't call toString() elsewhere. What's going on? Well, println() asks every object it is passed to use its toString() method, and then prints the string. If you don't write your own toString(), there is a default toString() that prints the object's class and location.

Another Constructor

Classes can have more than one constructor. This is called overloading. We are going to add a simpler constructor to class Ring, called a no-arguments constructor. It will provide default values for the two instance fields and update the static field. Here is the direct way to do it:

public Ring()
{
  metal = "iron";
  givenTo = "a human King";
  numberForged++;
}

We have no choices with this no-arguments constructor, but these defaults are just fine for creating the nineRingsOfMen[].

Another way of writing a no-arguments constructor that avoids mistakenly forgetting to do something like set a field (or increment a field) is to call the already-existing constructor inside the no-arguments constructor. Like this:

public Ring()
{
  this("iron", "a human King");  // calling the other constructor
}

The special way to call your own constructors is to use this as the first line of the constructor body. Not an obvious thing, so don't forget it.

Unless there are special circumstances, it is good style in Java to use the simplest way to accomplish a task, and that often means calling an existing method or constructor to accomplish most or all of the task for you.

  • Add this latter constructor to class Ring.

The following changes are for class Forge.

  • Create an array of 9 Rings called nineRingsOfMen[], all constructed with the no-arguments constructor.
  • Add println() for the first and last of the 9 Rings. Remember that arrays start at: __.
  • Add another println(Ring.forged()).
Personal tools
translate