Java 211 – Lecture IX Interfaces Yosef Mendelsohn An aside: More practice with arrays…. LetterCount.java The Die class used with an applet…. Die.java, DiceApplet.java Packages A package is a group of classes grouped together under a name. - All of the standard classes in Java are contained within packages. For example, the classes Math, String, and System are all contained in a package called “java.lang” One of the benefits of Java is the tremendous number of classes written by third party groups, (By ‘third-party’ we mean groups not necessarily affiliated with Sun, the creators of Java). Even the String class is not a de-facto part of the Java standard. However, its use is so obviously necessary for the development of even rudimentary programs, that this class gets shipped along with all standard implementations of Java. There are several similarly useful classes that you will automatically receive when you download the SDK. These classes make up the Java standard class library. The functionality provided by these libraries include mathamatical capabilities, networking capabilities, security functionality, applets, graphics, and others. A group of related classes is usually bundled into a package. Some packages in the Java standard class library include: java.applet, java.awt, java.math, java.lang, java.security, java.sql In order to use a class, we must first import it into our program. For example, to use the String class, we would need to import it from the java.lang package: import java.lang.String; Similarly, if we wanted to use the System class, we would import it using: import java.lang.System; Alternatively, we could import all of the classes in java.lang by issuing: import java.lang.*; As we’ve just discussed, the String class is part of the java.lang package. So is the ‘System’ class. However, notice that we did not have to use the ‘import’ statement discussed in the previous paragraphs. The reason is that since these classes are used so frequently, the ‘java.lang’ package is automatically included for us in every Java program. It is as if the line: import java.lang.*; is invisible, but present in every program. The ‘*’ after java.lang says to import all classes from the library (package). If we only wanted to use, say, the String class, we could instead type: import java.lang.String; But again, this is a moot point since as we said, the entire java.lang package is imported into every Java program. Let’s take a look at some other useful classes: See: - RandomNumbers.java (API Specification for Random class) - Price.java - CircleStats.java //makes use of the Loftus textbook’s keyboard class… Notice how in the CircleStats.java program there is an import statement that says: ‘import cs1.Keyboard;” . The problem here is that this package is not part of the Java standard library. Since we are making use of a non-standard library, we must have a copy of this package to put somewhere, where our compiler can make use of it. - This directory makes up the package. That is, to create a package, you simply take a number of related classes, and put them in a directory of whatever name you choose. The name you give to your directory becomes the name of the package. Static Methods: Invoking Without Instantiating Typically, to access a field or invoke a method, we need to specify an object. However, there is one exception: ‘static’ fields or methods. A method or field that is static can be accessed without requiring a calling object. Recall that whenever we specify a member variable (field) or a method of a class, we must specify an object to invoke the method (or on which to alter a field). For example, I can’t simply call the ‘toUpperCase()’ method of the String class without specifying a String object. However, there are some methods which can be invoked without specifying an object. These are called ‘static’ methods. For example, the Math class has a method called Random() (very similar to the Random class we saw in the java.util package). As discussed earlier, it is not uncommon in programming to want to get a random number from the program. If this method was not a static method, I would have to do the following: Math num = new Math(); double n = num.random(); By the way, the line Math num = new Math(); is not even allowed in Java. It is not possible to instantiate objects of type Math. (The explanation of why is beyond the scope of this lecture). In other words, we had to create a Math object for no other reason than to generate the random number we wish to put into ‘n’. We have no plans to use this object again. For this reason, the developers of the class made ‘random’ a static method. This means that you do NOT need an object to invoke the method. Simply providing the name of the class is sufficient: double n = Math.random(); In fact, in the example earlier, the statement Math num = new Math(); is illegal since you cannot instantiate a static class. This is the same thing we saw with the textbook’s Keyboard class earlier. To invoke any of the methods such as ‘readInt()’, ‘readString()’ etc, we need only specify the class name: String s = Keyboard.readString(); Look at the header for this method: static String readString() { … - See Echo.java Assignment: A big part of learning Java is learning how to make use of the many classes available to you. Take a look at and experiment with the DecimalFormat class that we made use of in the CircleStats.java program earlier. Try to find the API of the class from the link provided here. You can do a search in the ‘All Classes’ frame (lower right). More on the ‘static’ modifier Recall that if a method is declared static, this means that we can invoke the method without even having to have an instantiation of the class. Ordinarily to invoke a method, we must first specify an object. However, with a static method, we do not need to instantiate, and can invoke by simply qualifying the method name with the class (not object) name. System.out.println(“The square root of 27 is: “ + Math.sqrt(27) ); Here, the sqrt() method of the class Math is a static method. Therefore we can invoke it by simply providing the class name. In fact, all methods of the Math class are static. Note how we have always created main() with a static modifier. This is so that main()can be instantiated by the interpreter without having to instantiate an object from whatever class contains main(). Static Variables We’ve already discussed the ‘scope’ of variables. For example, a variable defined inside a method is “local” to that method. That is, the variable does not exist outside of that method. If you attempt to refer to variable outside of its scope, the compiler will give you an error. We’ve also seen variables that are tied to a class. For example, the StudentRecord class we discussed earlier in the quarter has a number of “instance variables” (fields) including: lastName, firstName, totalHwGrade, etc. These are called instance variables since the only way you can refer to them is via an instance (object) of the class. This makes sense, since, for example, the ‘lastName’ field only makes sense in the context of a StudentRecord object. For instance variables, each object of the class reserves memory for its own copy of that variable. However, there are times when you might want a variable to belong to all objects of the class. That is, you might want the variable to have the same value for all objects of the class. In this case, each object of the class shares the same memory space for the variable. If any one object changes the value of the variable, the value is changed for all objects of that class. These are called static variables. In the StudentRecord class, there is one such variable, ‘section’ that stores the section number for that class. All objects of type StudentRecord will have the same value for ‘section’. Question: Can you remember what the ‘final’ modifier stands for? Memory space for a static variable is reserved the first time the class is referenced by the program. (E.g. The first time the class is instantiated). Note that static methods can not refer to non-static variables. This is because non-static variables only exist in the context of an object. However, static methods do not exist in the context of an object, and therefore, know nothing about non-static (“instance”) variables. Static methods can, of course, access static variables. See CountInstances.java , and MyClass.java . ‘this’ Reference ‘this’ is a reserved word in Java. It is used when an object needs to refer to itself. For example, if you are inside a method and you need to refer to the calling object (the object that invoked the method), you can refer to that object by using the keyword ‘this’. We will discuss ‘this’ further as we progress. public boolean move(ChessPiece cp) { if (this.position == cp.position) // ‘this’ refers to the calling object return false; … more code… } Interfaces An interface is not a class, although it looks very similar. The formal definition for a Java interface is: “a collection of constants and abstract methods’. You are already familiar with the concept of a constant. An abstract method is simply a method without any definition. (I.e. An empty method). To declare an interface: public interface Speaker { public void method1(String s); public void method2(); } - Note the use of the word ‘interface’ instead of the word ‘class’. Note how the methods are not followed by implementation. Abstract Methods An abstract method is declared by giving the header of the method (i.e. visibility modifiers, method name, formal parameters), and following it by a semicolon (i.e. no braces). - By default, abstract methods have public visibility. - Abstract methods can be preceded by the word ‘abstract’, though within interfaces, they typically are not. Implementing an interface class To use an interface, the interface must be implemented. To implement an interface, a class must provide implementations for all of the methods listed in the interface. A class that implements an interface uses the reserved word ‘implements’. public class SomeClass implements SomeInterface { … Note: Implementing is not the same as extending. When you extend a class (as you have seen done with applets), you are using a technique called ‘Inheritance’. Implementing an interface is something different although there are a few similarities. Again, remember that the compiler will display errors if an implementation class fails to provide definitions for all of the methods in the interface. See: Speaker.java and Philosopher.java - The Speaker class (an interface), has only two methods. - The Philosopher class implements both methods listed in the Speaker interface. Those methods are defined with the same signatures (i.e. method names, and number and types of formal parameters) as the methods provided in the interface. - In addition, the Philosopher class has methods of its own. So when a class implements an interface, it “promises” that it will provide implementations for all of the methods listed in the interface. (However, this does not restrict the class from also adding other new methods on its own). - Multiple classes can implement the same interface. For example, the class Dog.java also implements the Speaker interface. A class can implement multiple interfaces at the same time. All it has to do is provide implementations for all of the methods contained in those interfaces. public class SomeClass implements Interface1, Interface2, Interface3 { ... All Philosopher objects can ‘speak’ and ‘announce’ since they are Speaker objects. (That is, Philosopher objects have these methods defined). Dog objects too are Speaker objects, and therefore can ‘speak’ and ‘announce’. Philosopher objects can also ‘pontificate’, though Dog objects cannot. In other words: All Dogs are Speakers All Philosophers are Speakers Speakers, however, are neither Philosophers, nor Dogs. Constants Recall that we defined an interface as being a collection of constants and abstract methods. So, we know that an interface may contain constants. When a class implements an interface, it gains access to all of the constants defined in the interface. This is a convenient way for multiple classes to share a group of constants. The Interface construct provides the basis for an important programming technique called polymorphism. Polymorphism (by using Interfaces) The term ‘polymorphism’ means “to have many forms”. A polymorphic reference is a reference that can refer to different types of objects. Interfaces are one way of allowing us to use polymorphism. Consider: Speaker s; //s is a reference to a Speaker s = new Dog(); //s is a reference to a Dog object s.speak(); //invokes the ‘speak’ method of the Dog class s = new Philosopher(); //s is a reference to a Philosopher s.speak(); //invokes the ‘speak’ method of the Philosopher class So at different times, the same reference ‘s’ can refer to completely different types of objects. Therefore, ‘s’ is a polymorphic reference. Notice how looking at the statement: s.speak(); in isolation does not allow us to determine which method of speak() will be invoked. An important yet subtle rule is illustrated here: It is the type of object rather than the type of reference that determines which method gets invoked. See: Talking.java Casting References At the end of Talking.java notice the statement: ((Philosopher) current).pontificate(); What we have had to do here is cast the reference ‘current’ to a Philosopher prior to invoking the pontificate() method. The reason we cannot simply invoke pontificate() directly is that ‘current’ is a reference to a Speaker. We can only invoke methods that are guaranteed to exist for the calling reference. (“calling reference” = the reference that invoked the method). If we wish to invoke other methods that are not guaranteed to exist, we must cast ‘current’ to the datatype which contains the method we wish to invoke. While this may seem to contradict the principle that it is the object that determines which method gets invoked, it does not. It is true that it is the object that determines which method gets invoked. However, for a reference to attempt to invoke a method, the method must first be guaranteed to exist. Since the method pontificate() is not guaranteed to exist for all Speaker objects, we must first cast ‘current’ to a reference where the method is guaranteed to exist.