views:

312

answers:

2

I've got a polymorphic array of objects which implement two (informal) interfaces. I want to be able to differentiate them with reflection along the lines of:

if (hasattr(obj, 'some_method')) {
    # `some_method` is only implemented by one interface.
    # Now I can use the appropriate dispatch semantics.
} else {
    # This must be the other interface.
    # Use the alternative dispatch semantics.
}

Maybe something like this works?:

if (*ref(obj)::'some_method') {
    # ...

I have difficulty telling when the syntax will try to invoke a subroutine and when it will return a subroutine reference. I'm not too familiar with package symbol tables ATM and I'm just trying to hack something out. :-)

Thanks in advance!

+11  A: 
use Scalar::Util qw(blessed);
if( blessed($obj) and $obj->can('some_method') ){ 

}

"can" here is a method inherited by all classes from UNIVERSAL . Classes can override this method, but its not a good idea to.

Also, "can" returns a reference to the function, so you can do:

$foo->can('some_method')->( $foo , @args );

or

my $sub = $foo->can('some_method'); 
$foo->$sub( @args );

Edit Updated Chain Syntax, thanks to Brian Phillips

Kent Fredric
Is `can` a reserved method name in some way, or do you just happen to shoot yourself in the foot if you implement it?
cdleary
Don't forget you need to explicitly pass the object in to the function reference returned by can: $foo->can('some_method')->($foo, @args). Alternatively, my $sub = $foo->can('some_method'); $foo->$sub(@args);
Brian Phillips
+1  A: 

Note that UNIVERSAL::can is not aware of AUTOLOAD methods. Here is a thread that discusses this in depth. See also UNIVERSAL::canAUTOLOAD and Class::AutoloadCAN.

Mark Johnson