[ Team LiB ] Previous Section Next Section

11.2 Testing Your Objects for Good Behavior

Besides providing a place for you to put universally available methods, the UNIVERSAL package comes preloaded with two very useful utility methods: isa and can. Because these methods are defined in UNIVERSAL, they are automatically available to all objects.

The isa method tests to see whether a given class or instance is a member of a given class or a member of a class that inherits from the given class. For example, continuing on with the Animal-family from the past chapters:

if (Horse->isa("Animal")) {    # does Horse inherit from Animal?
  print "A Horse is an Animal.\n";
}

my $tv_horse = Horse->named("Mr. Ed");
if ($tv_horse->isa("Animal")) { # is it an Animal?
  print $tv_horse->name, " is an Animal.\n";
  if ($tv_horse->isa("Horse")) { # is it a Horse?
    print "In fact, ", $tv_horse->name, " is a Horse.\n";
  } else {
    print "...but it's not a Horse.\n";
  }
}

This is handy when you have a heterogeneous mix of objects in a data structure and want to distinguish particular categories of objects:

my @horses = grep $_->isa("Horse"), @all_animals;

The result will be only the horses (or race horses) from the array. Compare that with:

my @horses_only = ref $_ eq "Horse", @all_animals;

which picks out just the horses because a RaceHorse won't return Horse for ref.

In general, you shouldn't use:

 ref($some_object) eq "SomeClass"

in your programs because it prevents future users from subclassing that class. Use the isa construct as given earlier.

One downside of the isa call here is that it works only on blessed references or scalars that look like class names. If you happen to pass it an unblessed reference, you get a fatal (but trappable) error of:

Can't call method "isa" on unblessed reference at ...

To call isa more robustly, don't call it as a method. Instead, call it as a subroutine:

if (UNIVERSAL::isa($unknown_thing, "Animal")) {
  ... it's an Animal! ...
}

This works regardless of what $unknown_thing contains.

As in the case of isa, you can test for acceptable behaviors with the can method. For example:

if ($tv_horse->can("eat")) {
  $tv_horse->eat("hay");
}

If the result of can is true, then somewhere in the inheritance hierarchy, a class has defined an eat method. Again, the caveats about $tv_horse being only either a blessed reference or a class name as a scalar still apply, so the robust solution when you might deal with nearly anything looks like:

if (UNIVERSAL::can($tv_horse, "eat")) { ... }

Note that if you defined UNIVERSAL::fandango earlier, then:

 $object->can("fandango")

always returns true because all objects can do the fandango.

    [ Team LiB ] Previous Section Next Section