Why shouldn't I use UNIVERSAL::isa?

前端 未结 7 1005
终归单人心
终归单人心 2021-02-07 07:58

According to this

http://perldoc.perl.org/UNIVERSAL.html

I shouldn\'t use UNIVERSAL::isa() and should instead use $obj->isa() or CLASS->isa().

This means

相关标签:
7条回答
  • 2021-02-07 08:20

    Everyone else has told you why you don't want to use UNIVERSAL::isa, because it breaks when things overload isa. If they've gone to all the habit of overloading that very special method, you certainly want to respect it. Sure, you could do this by writing:

    if (eval { $foo->isa("thing") }) {
         # Do thingish things
    }
    

    because eval guarantees to return false if it throws an exception, and the last value otherwise. But that looks awful, and you shouldn't need to write your code in funny ways because the language wants you to. What we really want is to write just:

    if ( $foo->isa("thing") ) {
         # Do thingish things
    }
    

    To do that, we'd have to make sure that $foo is always an object. But $foo could be a string, a number, a reference, an undefined value, or all sorts of weird stuff. What a shame Perl can't make everything a first class object.

    Oh, wait, it can...

    use autobox;   # Everything is now a first class object.
    use CGI;       # Because I know you have it installed.
    
    my $x = 5;
    my $y = CGI->new;
    
    print "\$x is a CGI object\n" if $x->isa('CGI');   # This isn't printed.
    print "\$y is a CGI object\n" if $y->isa('CGI');   # This is!
    

    You can grab autobox from the CPAN. You can also use it with lexical scope, so everything can be a first class object just for the files or blocks where you want to use ->isa() without all the extra headaches. It also does a lot more than what I've covered in this simple example.

    0 讨论(0)
  • 2021-02-07 08:23

    See the docs for UNIVERSAL::isa and UNIVERSAL::can for why you shouldn't do it.

    In a nutshell, there are important modules with a genuine need to override 'isa' (such as Test::MockObject), and if you call it as a function, you break this.

    I have to say, my $self = shift if UNIVERSAL::isa($_[0], __PACKAGE__) doesn't look terribly clean to me - anti-Perl advocates would be complaining about line noise. :)

    0 讨论(0)
  • 2021-02-07 08:25

    To directly answer your question, the answer is at the bottom of the page you linked to, namely that if a package defines an isa method, then calling UNIVERSAL::isa directly will not call the package isa method. This is very unintuitive behaviour from an object-orientation point of view.

    The rest of this post is just more questions about why you're doing this in the first place.

    In code like the above, in what cases would that specific isa test fail? i.e., if it's a method, in which case would the first argument not be the package class or an instance thereof?

    I ask this because I wonder if there is a legitimate reason why you would want to test whether the first argument is an object in the first place. i.e., are you just trying to catch people saying FooBar::method instead of FooBar->method or $foobar->method? I guess Perl isn't designed for that sort of coddling, and if people mistakenly use FooBar::method they'll find out soon enough.

    Your mileage may vary.

    0 讨论(0)
  • 2021-02-07 08:25

    Update for 2020: Perl v5.32 has the class infix operator, isa, which handles any sort of thing on the lefthand side. If the $something is not an object, you get back false with no blowup.

    use v5.32;
    
    if( $something isa 'Animal' ) { ... }
    
    0 讨论(0)
  • 2021-02-07 08:32

    The primary problem is that if you call UNIVERSAL::isa directly, you are bypassing any classes that have overloaded isa. If those classes rely on the overloaded behavior (which they probably do or else they would not have overridden it), then this is a problem. If you invoke isa directly on your blessed object, then the correct isa method will be called in either case (overloaded if it exists, UNIVERSAL:: if not).

    The second problem is that UNIVERSAL::isa will only perform the test you want on a blessed reference just like every other use of isa. It has different behavior for non-blessed references and simple scalars. So your example that doesn't check whether $ref is blessed is not doing the right thing, you're ignoring an error condition and using UNIVERSAL's alternate behavior. In certain circumstances this can cause subtle errors (for example, if your variable contains the name of a class).

    Consider:

    use CGI;
    
    my $a = CGI->new();
    
    my $b = "CGI";
    
    print UNIVERSAL::isa($a,"CGI");  # prints 1, $a is a CGI object.
    print UNIVERSAL::isa($b,"CGI");  # Also prints 1!! Uh-oh!!
    

    So, in summary, don't use UNIVERSAL::isa... Do the extra error check and invoke isa on your object directly.

    0 讨论(0)
  • 2021-02-07 08:32

    Right. It does a wrong thing for classes that overload isa. Just use the following idiom:

    if (eval { $obj->isa($class) }) {
    

    It is easily understood and commonly accepted.

    0 讨论(0)
提交回复
热议问题