12. Wrappers

Part of CS:2820 Object Oriented Software Development Notes, Spring 2021
by Douglas W. Jones
THE UNIVERSITY OF IOWA Department of Computer Science

 

Where were we?

Look what happened to our constructor to class Road:

    public Road( Scanner sc ) {
	// keyword Road was already scanned
	final String src;	// where does it come from
	final String dst;	// where does it go

	if (sc.hasNext()) {
	    src = sc.next();
	} else {
	    Error.warn( "road source missing\n" );
	    src = "???";
	}
	if (sc.hasNext()) {
	    dst = sc.next();
	} else {
	    Error.warn( "road " + src + " to missing destination\n" );
	    dst = "???";
	}
	if (sc.hasNextFloat()) {
	    travelTime = sc.nextFloat();
	} else {
	    Error.warn( "road " + src + " " + dst + " missing travel time\n" );
	    travelTime = Float.NaN;
	}
	destination = Intersection.lookup( dst );
	source = Intersection.lookup( dst );

        if (source == null) {
            Errors.warning(
                "In road " + src + " " + dst +
                ", Intersection " + src + " undefined"
            );
            // Bug:  Can we prevent creation of this object?
        }
        if (destination == null) {
            Errors.warning(
                "In road " + src + " " + dst +
                ", Intersection " + dst + " undefined"
            );
            // Bug:  Can we prevent creation of this object?
        }

	allRoads.add( this ); // this is the only place items are added!
    }

It's huge! We need to find a way to shrink it, and it would be nice to find a way to prevent the road from being constructed if it is defective.

Another Utility class?

Again and again, throughout our program, we wrote code like this:

	if (sc.hasNext()) {
	    dst = sc.next();
	} else {
	    Error.warn( "road " + src + " to missing destination\n" );
	    dst = "???";
	}

What we really want to write looks more like this:

	dst = sc.getNext( "???", "road " + src + " to missing destination\n" );

Where getNext is a method we wish we could add to class Scanner with the following semantics:

    public String getNext( String default, String msg ) {
        if (this.hasNext() ) return next();
	Errors.warn( msg );
        return default;
    }

If we had our way, we could declare a new class:

class myScanner extends Scanner {

    public String getNext( String default, String msg ) {
        if (this.hasNext() ) return next();
	Errors.warn( msg );
        return default;
    }

    public Float getNextFloat( Float default, String msg ) {
        if (this.hasNextFloat() ) return nextFloat();
	Errors.warn( msg );
        return default;
    }

Unfortunately, Java forbids us to do this! Class Scanner is a final class. That makes it illegal to extend the class with a subclass. As a result, we need to create what is called a wrapper or adapter class that presents the scanner operations we want in terms of the scanner operations we have

Wrapper or Adapter Classes

Here is a wrapper class for scanners:

class myScanner {
    Scanner self; // each myScanner wraps a scanner

    public boolean hasNext() {
        return self.hasNext();
    }

    public String next() {
        return self.next();
    }

    public String getNext( String default, String msg ) {
        if (this.hasNext() ) return next();
	Errors.warn( msg );
        return default;
    }
}

Wrappers are extremely common! Java provides class Integer as a wrapper for the type int because the latter primitive type is not really a class. Then Java does everything it can to prevent programmers from knowing that it did this by mechansims called autoboxing and unboxing so that, if you use an int where an Integer is expected, it is automatically boxed up in a wrapper object, and if you use an Integer where an int was expected, it is automatically unboxed.

Another common wrapper is the Java class File. The underlying operating system has files, with a file interface that does much of what Java's File objects do, but the interface is not object-oriented, or to the extent it is, it does not match Java's object model. To deal with this, the Java designers created class File, where every file object is "wrapped around" an open file in the underlying operating system.

Wrapper or interface classes are very commonly used to make portable code. The window managers of most modern systems are at least somewhat object-oriented, but they are incompatible. Window libraries on languages like Java exist as wrappers for the lower level window manager.

In terms of virtual machine hierarchies, wrapper classes are transparent virtual machine layers inserted between a lower level system and its users. The wrapper does not prevent using the lower level primitives, all it does is re-package them for higher level code.