Chapter:Classes - final members

From Juneday education
Jump to: navigation, search

Meta information about this chapter

Expand using link to the right to see the full content.

Introduction

This chapter is a part of the suite of chapters about classes. Since the final keyword is likely to turn up when the students read source code, we decided to include a short chapter on the topic. We think it is good to try and cover most parts of a Java class, including various modifiers such as the final keyword.

Purpose

This chapter is included for completeness. The final keyword is something the students will encounter in other programmers' code, so it is important that they have some basic understanding of the purpose of the keyword.

Since the keyword can occur at different levels and contexts of a class' source code, and with different meanings and implications, it is also important that the students understand the various uses of the keyword.

We don't think this is very important knowledge which should be examined and tested, but we do believe that it is important enough to have been exposed to, and have a basic understanding of.

Goal

After this chapter, the student shall understand:

  • The meaning of a final static variable
  • The meaning of a final instance variable
  • That methods and the class itself can be declared final, but more on that comes in later chapters
  • That local variables too can be declared final and what that means

Instructions to the teacher

Common problems

We realize that it is hard to discuss final as a modifier of classes and methods, before we have introduced inheritance properly. Inheritance is a topic for later chapters in the book, but we think it is worth the risk to mention that classes and even methods can be final too here, and refer to later chapters on inheritance.

The focus should be on variables though.

A problem here might be to discuss with the students or explain to the students that local variables are actually not members of a class. This is a topic which is a very common source of confusion when teaching Java.

Just like it is important to remind the students of the fact that local variables don't get default values, it is important here to point out again that local variables are not part of the class per se, they are part of parts of the class. Local variables are just that - local. A final local variable cannot be changed after its initialization in the local scope. Since it is not visible (or probably doesn't even exist) after the execution has left the method (or other local scope), the variable isn't reachable anyway, so the final modifier indeed only is relevant within the scope of the local variable.

Other variables, namely static or instance variables, are in scope in much more places in the class, compared to local variables. That's something worth mentioning to or discussing with the students, when talking about e.g. final static variables (which if they are public are accessible even outside the class!).

Another thing which might be hard for the students to grasp, is the concept of reference variables combined with the final modifier.

Imagine a final instance variable private final List<String> names; which is initialized in the constructor. If the class exposes the list, e.g. via an accessor method public List<String>names() { return names; }, then the contents of the list can be changed, even if there is no way to re-assign the names variable to a completely new list reference!

Exposing a final reference to an immutable object, however, like a String, is not problematic at all. Even if other objects can get a reference to a final String, they cannot modify it, since Strings are immutable.

This is, in our experience, a challenge to explain to the students, which it is important to not shy away from the discussion. If the topic comes up, use the whiteboard and recap the concepts of references to objects in the heap, etc.

Introduction

Note: Final variables, when used as global constants, are mentioned in the video for static variables.

Note: a method and a class can also be final but this does not make sense before we introduce inheritance so we'll skip it for now

Final instance variables

A final instance variable of a class, can't have its value changed after it is initialized. This is typically used when you want to create a class which is immutable, like java.lang.String for instance. Having the instance variables declared final, means that users of the class can trust that the value of a variable will never change. Typically, you set the value once and for all in the constructor, when you have final instance variables. See Chapter:Classes_-_Strings_are_immutable for more information (later in the book).

Final methods

A method which is declared final cannot be changed by a class which inherits the class with the final method. You'll read more about that in the chapters about inheritance.

Final static variables

A static variable which is declared final, cannot be re-assigned with a new value after initialization, just like final instance variables. A typical use of final static variables are for constant values. The Java API is full of examples of this.

Let's say, you are writing an application for invoices. The application should calculate the payment due date, and has three values (for different kinds of customers): Normal customers have 10 days, gold customers have 20 days and platinum customers have 30 days before payment due date.

A class could define three global constants using public static final:

public class PaymentTerms {
  public final static int NORMAL_CUSTOMER_PAYMENT_DAYS = 10;
  public final static int GOLD_CUSTOMER_PAYMENT_DAYS = 20;
  public final static int PLATINUM_CUSTOMER_PAYMENT_DAYS = 30;
  ...
}

Now, code in the Invoice class could calculate the payment due date using those constants:

...
switch (CUSTOMER_TYPE) {
  case NORMAL:
  days = PaymentTerms.NORMAL_CUSTOMER_PAYMENT_DAYS;
  break;
  case GOLD:
  days = PaymentTerms.GOLD_CUSTOMER_PAYMENT_DAYS;
  break;
  ... etc etc...  
}

Lately, however, it is more common to use the enum construct for this. An enum has all the benefits of constants, plus it can have constructors and methods. Using the example above, an enum could hold both customer type by its name and the payment days as a method:

// simplified example where the switch is on an enum:
switch (customer.TYPE) {
  case GOLD:
  days = customer.TYPE.paymentDays();
  break;
  ... etc ... etc ...
}

Pitfall! Static constants may be compiled into client classes

Here's an example from the Java Language Specification which warns about the fact that final static variables' values may be compiled into client classes as values (literals).

Consider a class Settings where we have various constants:

public class Settings {
  public static final boolean DEBUG = true;
}

The idea is that we can now have "conditional compiling" of a client class, which does debug printouts only if the flag DEBUG is true and otherwise does nothing:

public class Client {
  public static void main(String[] args) {
    if (Settings.DEBUG) {
      System.out.println("Debug should be done");
    }
  }
}

If we compile both classes, the client class will print the debug message. Next, we want to turn off debug printing, and change the Settings class constant DEBUG to false and re-compile the file Settings.java. Do we have to also re-compile Client.java?

The pitfall here is that we don't know. It is very likely that because the compiler saw that DEBUG was true and a final variable (constant), then it emitted true directly into the class file for Client as a literal. Because of this, we then have to re-compile also Client.java after each re-compilation of Settings.

Feel free to try this!

  • Create the two files as shown above
  • Compile both files
  • Run java Client and see the debug message being printed
  • Change DEBUG to false and re-compile only Settings.java
  • Run java Client again and see the debug message still being printed
  • Re-compile Client.java and run the program again, and see the debug message not being printed

How can we force the compiler to use the actual value of the constant and not save the literal value?

The reason for the compiler being allowed to "cache" the literal value of DEBUG into the class bytecode of Client, is that it can be sure that a final variable won't change during runtime (when the program is running). The compiler sees that the constant is initialized with a literal value (e.g. true), and is then allowed to insert the literal value into Client's class file.

But if we would instead intialize DEBUG's value with a method call, things (and rules) are different. Then the compiler isn't allowed to use a literal value (because the compiler can't predict what value a method call will produce, even if we sometimes can!).

So if we change Settings.java to this, what will happen?

public class Settings {
  public static final boolean DEBUG = Boolean.valueOf(true).booleanValue();
}

The compiler sees that the final variable is initialized, and is OK with that. But it also sees that the constant is initialized using a method call (actually a chain of method calls, but one would be enough). Therefore, it is not allowed to store any literal value in place of the constant being used in other classes, because it cannot know what value a method will return. The compiler never tries to calculate what value a method will return.

Now, we can change the value of debug (by changing the method call initialization to Boolean.valueOf(false).booleanValue(); and re-compile Settings.java. We don't have to re-compile Client.java in order for it in runtime to "see" that the value of DEBUG is false. Nothing is cached in the class bytecode for Client.

Try it!

Note: You can also use the shorter auto-unboxing expression Boolean.valueOf(false), since the wrapper class (also known as "boxing class") Boolean will automatically be unboxed to the primitive boolean type.

Local final variables

Local variables are not "members" of a class, but occur rather inside constructors and methods of a class. A final local variable cannot be changed, which signals to programmers reading the code that they don't have to worry about some variable being changed further down in the code. It is not uncommon to make parameters to methods final.

Head on to the next chapter with exercises!

Chapters about classes

Here are the chapters about classes in this book, so that you can keep a check on your progress:

Videos and slides

Java Classes Final (Full playlist) | Java Classes Final | Classes - Final members.pdf

Links

Further reading

Where to go next

Next page has exercises for this chapter: Classes - final members - Exercises

« PreviousBook TOCNext »