Methods
Goals
- Learn about methods, parameters, and return values.
- Know the difference between static and instance methods of a class.
- Work with access level modifiers.
- Recognize JavaBeans conventions.
Concepts
- access level modifier
- accessor method
- caller
- class methods
- data hiding
- getter
- import
- instance method
- method
- method overloading
- JavaBeans
- package-level access
- private access
- protected access
- public access
- return value
- setter
- static import
- varargs
- visibility
Language
import
import static
private
protected
public
return
static
Javadoc
@link
@return
Library
java.lang
java.lang.Double
java.lang.Double.POSITIVE_INFINITY
java.lang.Math.abs(int a)
java.util
java.util.Random
java.util.Random.nextInt()
java.security
java.security.SecureRandom
Lesson
In the previous lesson on classes you learned that the main purpose of objects are to encapsulate data and functionality. In that lesson you learned how to include instance variables which live independently with each instance of the class, and how these instance variables are different from static variables which only have one instance, associated with the class itself. You also learned how to use a constructor to initialize instance variables each time a class is instantiated. The following example class encapsulates the x
and y
values of a two-dimensional geometric point.
Instance Methods
So far this description of classes has only covered the data portion of encapsulation. For encapsulating functionality, classes provide methods, which allow arguments and optionally return a value.
The methods you have learned about so far included the word static
. If a method does not use the static
keyword, it is called an instance method and can access the instance variables of the class as if those variables were the only ones that existed. Other instances of the class will have separate copies of those same instance variables. Instance methods know how to access only the instance variables of the instance on which they were invoked, because behind the scenes they use the equivalent of the this
“variable” you learned about earlier.
The following example shows how a geometrical Point
class might provide an instance method for moving the point's location, so that the program calling the method (the caller) does not have to make these calculations itself and worry about updating the point's internal variables. The method has encapsulated the functionality of moving the point.
An instance method can optionally provide a return value to the caller. Here for example is a method that calculates the slope between one point and another point.
Class Methods
Just as a class can have instance variables and class variables, a class can also have instance methods and class methods. Also called “static methods”, these are the methods you created in previous lessons. Like class variables, class methods are declared by using the static
keyword. Class methods most closely resemble the old-fashioned “procedures” and “functions” of non-object oriented languages, but in Java they are still grouped inside a class. To invoke a class method, you must use the name of the class followed by a dot .
character and the name of the method, such as Foo.bar()
.
Class methods can provide general functionality that does not apply to any single instance of the class. They also provide a way to collect functionality related to instances of other classes. For example, think about the Point.calculateSlope()
method above. Is it an expected behavior that a point would know how to calculate its slope compared with another point? Or is the concept of “slope” something that an individual point wouldn't know about, and instead some higher level entity might calculate instead? Design questions like this will be left to future lessons, but now as an example the calculateSlope()
method is rewritten below as a class method of the Geometry
class, which we'll use as a way to group together some static “utility methods”.
Varargs
You've already learned about varargs, and they can come in useful when working with your own custom classes. For example, here is a way to print a sequence of points, if you don't know how many points there are to print. Because this is not confined to a single point, we add it as a static utility method to the Point
class itself. Remember that behind the scenes, Java will automatically create and pass an array, and the method can treat the argument as it would an array.
Imports
If you wish to use a class from another package, such as those that come with the Java standard library, you can always use that class by typing its full name including package. For example there is a class named Random
in the java.util
package, which comes with the Java standard library. An instance of the class allows the caller to generate a stream of pseudorandom numbers by calling one or more of its methods.
The following snippet would print out three random numbers. Note how the Random
class is identified by its full name java.util.Random
.
Rather than entering the full name every time you access a class, you can can inform Java of which classes you intend to use at the beginning of your .java
file. This is referred to as importing classes, and you do this with the import
statement, naming the class that you would like to use.
Here is how you could use Random
to generate some random points and print out their slope, using the method java.util.Random.nextInt()
to generate random integers. The program uses the Point
and Geometry
classes defined above.
Static Imports
A static import works a little differently than a class import. Rather than informing Java that you wish to use a class without needing to specify its package each time, the static import indicates that you want to use a static variable or static method in a class without needing to specify the class name each time. This can be used even for classes in the same package, as the static import is removing the need to indicate the class name, not the package name. A static import is similar to a class import, except that it uses import static
and identifies the package, class, and member (variable or method) to be statically imported.
Take for example the static Geometry.slope(…)
method we wrote above. Using static import we can rewrite our RandomPointPrinter
so that we can simply say slope(…)
instead of Geometry.slope(…)
. The same goes for the method Point.print(…)
and the constant Geometry.UNDEFINED_SLOPE
.
Access Level Modifiers
Package (Default) Access
Until this point we have been placing our custom classes such as Point
and Geometry
in the same package com.example
as the program using those classes. This works because, by default, Java allows what is called package-level access to other classes and their members.
The access level (also called visibility, because it determines what other classes can "see" as far as the compiler is concerned) can be changed at two different places in a program:
- at the class level (
public
or package-default), and/or - at the class member level (
public
,protected
,private
, or package-default)
The class level modifier sets the limit on visibility; regardless of a member's visibility setting, it can never be more visible than that of the class.
Public Access
But you have seen that we are also able to access classes outside our program package, such as java.lang.Double
and java.util.Random
. This is because the classes in those packages use an access level modifier to specify public access, both at the class level and at the method level. In fact you've seen public access at work in main(…)
method of the “Hello World” program in the first lesson.
Notice that the class is declared public
so that it can be used by the world at large. If a class is declared as public
, classes from other packages can access that class, but they still cannot call its methods unless they too are declared as public
.
The remaining access levels apply only members (variables and methods of a class, both instance members and static members).
Protected Access
Protected access prevents members from being accessed outside their package, except by subclasses (in whatever package they reside). This access uses the keyword protected
instead of public
. You will learn more about subclasses in a future lesson, and we will explore protected-level access in more detail then.
Private Access
Using the private access level prevents members from being access by other classes, even those within the same package! A class would usually mark a method private
if that method performed some temporary calculation that should not be available to the world at large.
Data Hiding
The program design concept of data hiding goes hand-in-hand with the object-oriented concept of encapsulation. A class not only encapsulates or bundles together related data and functionality, it can also hide that data and make it available only on a need-to-know basis, requiring access to go through the proper channel (e.g. through public methods). This makes the program safer by preventing inadvertent modification of an object's variables, or by ensuring that certain “side effects” always take place when values are changed.
One example might be a bank account. For security of the account we would not want all parts of the program to be able to manipulate the long
value holding the account balance. We would want to account to only be accessed via methods such as deposit(long amount)
, which would ensure that "side effects" (such as sending an email confirmation) were always performed.
JavaBeans
With access modifiers classes can do a good job of hiding information. If they want to expose information, they can provide accessor methods. There is sometimes a need for other classes to discover which information a class is exposing. For example, when assembling components in a user interface tools need to know that a particular UI component allows its color to be changed.
Other languages have added language features that allow the developer to define “properties” that hide the actual storage yet force access to go through property methods. Java did not add property support to the language; instead it created a set of conventions: the JavaBeans component model. To create a “JavaBean” a developer follows the following pattern:
- Actual instance variables such as
foo
are kept private. - If a value should be considered a property, a getter method is added using the form
getFoo()
. If the property represents aboolean
value, the formisFoo()
is used. - If the property can be changed, a setter method is added using the form
setFoo(Foo foo)
. - The class usually has a no-argument constructor.
Here is how we could rewrite the Point class as a JavaBean:
Now other classes would be able to see that the Point
class has x
and y
properties because of its getter and setter methods. Tools would be able to create points and allow the x
and y
properties to be modified in a user interface builder, for example.
Review
Gotchas
- If your method does not return a value, it must use the word
void
in place of a return type to indicate this.
In the Real World
- Even though modern programs only create actual “JavaBeans” in specialized use cases, the concept of data hiding is still of the utmost importance, and you will still frequently see the terminology “getters” and “setters”.
- The
java.util.Random
class should not be used for most real-world applications, as it might be possible for the sequence it generates to be guessed; and it doesn't perform well with multiple thread access. For more robust random number generation, e.g. for use in cryptography, usejava.security.SecureRandom
The API documentation forjava.util.Random
provides more information and recommendations.
Think About It
- Do I want to make this method available to the general public? Do I want to support it as a public API? If not, I may want to declare it
protected
or evenprivate
.
Self Evaluation
- How would you prevent a class from being instantiated? How might that be useful?
Task
Update your Book
class to use methods and data hiding.
- Make all the variable members private.
- Provide "getter" (i.e.
getXXX()
) methods for accessing the main attributes of the book. - Add a method
getYearsInPrint()
that tells the number of years the book has been in print.- For now, if the publication date has a range, use the first number in the range.
- Use a constant to indicate the current year.
- Add an instance method to provide the absolute number of years difference between publication date of that book and another book.
- Use the
Math.abs()
method. - Use a static import.
- Use the
- In the main application class
Booker
, you already have a static method that prints a single book. Create an additional static method (an additional layer of indirection) for printing all the books.- Use a varargs parameter to pass all the available books to the method.
- The method will then delegate to your existing method for printing each single book.
- Call the method from the main application, passing it the array of books.
See Also
- Methods (Oracle - The Java™ Tutorials)
- Understanding Class Members (Oracle - The Java™ Tutorials)
- Varargs (Oracle - Java™ Platform Overview)
- Using Package Members (Oracle - The Java™ Tutorials)
- Controlling Access to Members of a Class (Oracle - The Java™ Tutorials)
- Writing JavaBeans Components (Oracle - The Java™ Tutorials)
- JavaBeans (WikiBooks)