Software Engineering
Interfaces, Inner Classes, and Annotations

Course Map

Agenda


Using Interfaces for Code Reuse

Using Interfaces for Code Reuse

Using Interfaces for Code Reuse

Interfaces vs. Classes

An interface type is similar to a class, but there are several important differences:

Generic DataSet for Measurable Objects

public class DataSet
{
   . . .
   public void add(Measurable x)
   {
      sum = sum + x.getMeasure();
      if (count == 0 || maximum.getMeasure() < x.getMeasure())
         maximum = x;
      count++;
   }

   public Measurable getMaximum()
   {
      return maximum;
   }

   private double sum;
   private Measurable maximum;
   private int count;
}

Implementing an Interface Type

UML Diagram of DataSet and Related Classes

Syntax: Defining an Interface

 
public interface InterfaceName
{
   // method signatures
}

Example:

 
public interface Measurable
{
   double getMeasure();
}

Purpose:

To define an interface and its method signatures. The methods are automatically public.

Syntax: Implementing an Interface

 
public class ClassName
   implements InterfaceName, InterfaceName, ...
{
      // methods
      // instance variables
}

Example:

 
public class BankAccount implements Measurable
{
   // Other BankAccount methods
   public double getMeasure()
   {
      // Method implementation
   }
 }

Purpose:

To define a new class that implements the methods of an interface

File DataSetTester.java


Output
   Average balance = 4000.0
   Highest balance = 10000.0
   Average coin value = 0.13333333333333333
   Highest coin value = 0.25

Self Check

  1. Suppose you want to use the DataSet class to find the Country object with the largest population. What condition must the Country class fulfill?
  2. Why can't the add method of the DataSet class have a parameter of type Object?

Answers

  1. It must implement the Measurable interface, and its getMeasure method must return the population
  2. The Object class doesn't have a getMeasure method, and the add method invokes the getMeasure method

Converting Between Class and Interface Types

Casts

Self Check

  1. Can you use a cast (BankAccount) x to convert a Measurable variable x to a BankAccount reference?
  2. If both BankAccount and Coin implement the Measurable interface, can a Coin reference be converted to a BankAccount reference?

Answers

  1. Only if x actually refers to a BankAccount object.
  2. No–a Coin reference can be converted to a Measurable reference, but if you attempt to cast that reference to a BankAccount, an exception occurs.

Polymorphism

Polymorphism

Self Check

  1. Why is it impossible to construct a Measurable object?
  2. Why can you nevertheless declare a variable whose type is Measurable?
  3. What do overloading and polymorphism have in common? Where do they differ?

Answers

  1. Measurable is an interface. Interfaces have no fields and no method implementations.
  2. That variable never refers to a Measurable object. It refers to an object of some class–a class that implements the Measurable interface.
  3. Both describe a situation where one method name can denote multiple methods. However, overloading is resolved early by the compiler, by looking at the types of the parameter variables. Polymorphism is resolved late, by looking at the type of the implicit parameter object just before making the call.

Using Interfaces for Callbacks

Using Interfaces for Callbacks

Using Interfaces for Callbacks

UML Diagram of Measurer Interface and Related Classes

File DataSet.java

File DataSetTester2.java

File Measurer.java

File RectangleMeasurer.java


Output
   Average area = 616.6666666666666
   Maximum area rectangle = java.awt.Rectangle[x=10,y=20,width=30,height=40]

Self Check

  1. Suppose you want to use the DataSet clas to find the longest String from a set of inputs. Why can't this work?
  2. How can you use the DataSet class of this section to find the longest String from a set of inputs?
  3. Why does the measure method of the Measurer interface have one more parameter than the getMeasure method of the Measurable interface?

Answers

  1. The String class doesn't implement the Measurable interface.
  2. Implement a class StringMeasurer that implements the Measurer interface.
  3. A measurer measures an object, whereas getMeasure measures "itself", that is, the implicit parameter.

Nested Types (Inner Classes) and Top-Level Classes


Inner Classes

Restrictions on Nested Top-Level Classes

Restrictions on Member Classes

Restrictions on Local Classes

Syntax: Inner Classes

  Declared inside a method (local class)
class OuterClassName
{
  method signature
  {
     . . .
     class InnerClassName
     {
        // methods
        // fields
     }
     . . .
  }
  . . .
}
  Declared inside the class (member class)
class OuterClassName
{
   // methods
   // fields
   accessSpecifier class InnerClassName
   {
      // methods
      // fields
   }
   . . .
}

Purpose:

To define an inner class whose scope is restricted to a single method (local class) or the methods of a single class (member class).

File DataSetTester3.java

Self Check

  1. Why would you use an inner class instead of a regular class?
  2. How many class files are produced when you compile the DataSetTester3 program?

Answers

  1. Inner classes are convenient for insignificant classes. Also, their methods can access variables and fields from the surrounding scope.
  2. Four: one for the outer class, one for the inner class, and two for the DataSet and Measurer classes.

Anonymous Classes

Coin aCoin = new Coin(0.1, "dime");
data.add(aCoin);

or

data.add(new Coin(0.1, "dime"));
public static void main(String[] args) {
	// Construct an object of an anonymous class
	Measurer m = new Measurer()
		// Class definition starts here
		{
			public double measure(Object obj) {
				Rectangle rec = (Rectangle)obj;
				double area = rec.getWidth() * rec.getHight();	
				return area;
			}
		};
	DataSet data = new DataSet(m);
	. . .
}	

When to use Anonymous Classes

Syntax: Anonymous Classes

 
new class-Name ( [argument-list] ) 
{
  class-body
}

or

new interface-Name () 
{
  class-body
}

Example:

 
Measurer m = new Measurer()
    {
	public double measure(Object obj) {
		Rectangle rec = (Rectangle)obj;
		double area = rec.getWidth() * rec.getHight();
		return area;
	}
    };

Purpose:

To define a one-shot class exactly where it is needed.

Processing Timer Events

Example: Countdown

File TimerTester.java

Self Check

  1. Why does a timer require a listener object?
  2. How many times is the actionPerformed method called in the preceding program?

Answers

  1. The timer needs to call some method whenever the time interval expires. It calls the actionPerformed method of the listener object.
  2. It depends. The method is called once per second. The first eleven times, it prints a message. The remaining times, it exits silently. The timer is only terminated when the user quits the program.

Accessing Surrounding Variables

Accessing Surrounding Variables

File TimerTester2.java

File TimerTester3.java

Output

java.awt.Rectangle[x=6,y=11,width=20,height=30]
java.awt.Rectangle[x=7,y=12,width=20,height=30]
java.awt.Rectangle[x=8,y=13,width=20,height=30]
. . .
java.awt.Rectangle[x=28,y=33,width=20,height=30]
java.awt.Rectangle[x=29,y=34,width=20,height=30]
Last box position: java.awt.Rectangle[x=29,y=34,width=20,height=30]

Self Check

  1. Why would an inner class method want to access a variable from a surrounding scope?
  2. If an inner class accesses a local variable from a surrounding scope, what special rule applies?

Answers

  1. Direct access is simpler than the alternative–passing the variable as a parameter to a constructor or method.
  2. The local variable must be declared as final.
 

Why Inner Classes?

 

Annotations - the Java's Choice for Metadata

Metadata can be used to create documentation, to track down dependencies in code, and even to perform rudimentary compile-time checking.

Annotation formats:

 

The Override, Deprecated, and SuppressWarnings Annotation Types

public class OverrideTester {

  public OverrideTester() { }

  @Override
  public String toString() {
    return super.toString() + " [Override Tester Implementation]";
  }

  @Override
  public int hashCode() {
    return toString().hashCode();
  }
}


public class DeprecatedClass {

  @Deprecated public void doSomething() {
    // some code
  }

  public void doSomethingElse() {
    // This method presumably does what doSomething() does, but better
  }
}



@SuppressWarnings(value={"unchecked"})
public void nonGenericsMethod() {
  List wordList = new ArrayList();    // no typing information on the List

  wordList.add("foo");                // causes error on list addition
}

Defining and Using Marker Annotation Types

  1. Defining

    /**
     * Marker annotation to indicate that a method or class
     *   is still in progress.
     */
    public @interface InProgress { }
     

  2. Using

    @InProgress
    public void calculateInterest(float amount, float rate) {
      // Need to finish this method later
    }

Defining Members of  Annotation Types

/**
 * Annotation type to indicate a task still needs to be
 *   completed.
 */
public @interface TODO {
  String value();
}

Shorthand version (single-value annotations):

@InProgress
@TODO("Figure out the amount of interest per month")
public void calculateInterest(float amount, float rate) {
  // Need to finish this method later
}

The shorthand version is available only if the annotation type has a single-member variable named value.

Longhand version (multivalue annotations):

@InProgress
@TODO(value="Figure out the amount of interest per month")
public void calculateInterest(float amount, float rate) {
  // Need to finish this method later
}
 

Defining Default Values in Annotation Types

public @interface GroupTODO {

  public enum Severity { CRITICAL, IMPORTANT, TRIVIAL, DOCUMENTATION };

  Severity severity() default Severity.IMPORTANT;
  String item();
  String assignedTo();
  String dateAssigned();
}

Overriding default values

@GroupTODO(
  severity=GroupTODO.Severity.DOCUMENTATION,
  item="Need to explain how this rather unusual method works",
  assignedTo="Mike Sobolewski",
  dateAssigned="04/10/2006"
)
public  void reallyConfusingMethod(int codePoint) {
  // Really weird code implementation
}
 

Annotating an Annotation - Meta-annotations

 

Using Target Meta-annotation

import java.lang.annotation.ElementType;
import java.lang.annotation.Target;

/**
 * Annotation type to indicate a task still needs to be completed
 */
@Target({ElementType.TYPE,
         ElementType.METHOD,
         ElementType.CONSTRUCTOR,
         ElementType.ANNOTATION_TYPE})
public @interface TODO {
  String value();
}
 

 

Using Retention Meta-annotation

@Retention(RetentionPolicy.SOURCE)
public @interface SuppressWarnings {
  // annotation type body
}

 

Using Documented Meta-annotation

import java.lang.annotation.Documented;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Marker annotation to indicate that a method or class
 *   is still in progress.
 */
@Documented
@Retention(RetentionPolicy.RUNTIME)
public @interface InProgress { }

 

Using Inherited Meta-annotation

import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * Marker annotation to indicate that a method or class
 *   is still in progress.
 */
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
public @interface InProgress { }
 

 

Annotations and Reflection

Tie It All Together - Build a Simple Annotation-based Test Framework

public class Foo {
    @Test public static void m1() { }

    public static void m2() { }

    @Test public static void m3() {
        throw new RuntimeException("Boom");
    }

    public static void m4() { }

    @Test public static void m5() { }

    public static void m6() { }

    @Test public static void m7() {
        throw new RuntimeException("Crash");
    }

    public static void m8() { }
}
import java.lang.reflect.*;

public class RunTests {
   public static void main(String[] args) throws Exception {
      int passed = 0, failed = 0;
      for (Method m : Class.forName(args[0]).getMethods()) {
         if (m.isAnnotationPresent(Test.class)) {
            try {
               m.invoke(null);
               passed++;
            } catch (Throwable ex) {
               System.out.printf("Test %s failed: %s %n", m, ex.getCause());
               failed++;
            }
         }
      }
      System.out.printf("Passed: %d, Failed %d%n", passed, failed);
   }
}
$ java RunTests Foo
Test public static void Foo.m3() failed: java.lang.RuntimeException: Boom 
Test public static void Foo.m7() failed: java.lang.RuntimeException: Crash 
Passed: 2, Failed 2

 

JUnit 4 Annotation Types

@Retention(value=RUNTIME)
@Target(value=METHOD)
public @interface Test

A simple test looks like this:
public class Example {
  @Test public void method() {
    System.out.println("Hello");
  }
}

 

The Annotation Processing Tool (apt)