pileofrogs has asked for the wisdom of the Perl Monks concerning the following question:

In my brief Java experience many years ago, I ran across what they called 'interfaces'. These are classes that describe a set of methods but don't actually define them. Instead, each of those methods must be overridden by any subclass. Failure to override those methods results in a compile time error.

An example might be a 'Shape' class as an interface. Every shape has an area, but every shape ('circle', 'square', 'triangle'...) computes that area differently. With an interface, you enforce the requirement that any subclasses have an 'area' method. If ($circle->isa('Shape')), you know it's going to have an 'area' method written specifically for whatever kind of shape that is...

As I'm working in perl, I keep trying to build things that do that. I'm dumb enough to build a class and forget to override that one rarely called but vital method. So, I like things that yell at me during compile time.

Any suggestions?

Thanks!

--Pileofrogs

Update: The key to what I want is that it complains at compile time, or at least object initialization. Complaining when I attempt to call the method is too late...

Replies are listed 'Best First'.
Re: Java Style Interface Objects?
by stvn (Monsignor) on Feb 03, 2006 at 17:59 UTC
    As I'm working in perl, I keep trying to build things that do that. I'm dumb enough to build a class and forget to override that one rarely called but vital method. So, I like things that yell at me during compile time

    Well, it won't yell at you during compile time, but I wrote Class::Interfaces for just this thing. However, a couple of things to note about Java-style interfaces in Perl.

    • Perl does not do type checking1, so all interface polymorphism you do has to be done manually
    • Perl allows a high degree of runtime manipulation, so even if you do check the interface at compile time, there is no guarentee it won't get mucked with in runtime.

    (1) - Some people here will tell you that Perl is strongly typed because it has Scalars, Arrays and Hashes which are themselves strongly typed. However, they also perform a number of automatic conversions (calling a list in scalar context) which can (at times) defeat the stong typing. And most relevant to this topic, Perl does not strongly type objects.

    -stvn
Re: Java Style Interface Objects?
by Tanktalus (Canon) on Feb 03, 2006 at 18:13 UTC

    If you want to validate your object, just check!

    sub new { #... bless $self, $class; for my $check (qw(func1 func2 rarelyused)) { die "$class doesn't have method '$check'" unless $self->can($check +); } }

    Of course, even then, if $class actually has an AUTOLOAD function, can doesn't play nice with that, and you end up thinking it can't do something when it can, just fine, through the AUTOLOAD method.

      Of course, even then, if $class actually has an AUTOLOAD function, can doesn't play nice with that...

      Sure it does. Predeclare your subs or write your own can() method.

Re: Java Style Interface Objects?
by izut (Chaplain) on Feb 03, 2006 at 17:38 UTC
    One approach you have is die() on interface's method signature.
    sub method_i_should_overload { die "Overload this!"; }
    But I really don't know why you want do that. Perl will look every @ISA for the method you're looking for. You won't need that on Perl.

    Igor 'izut' Sutton
    your code, your rules.

Re: Java Style Interface Objects?
by chromatic (Archbishop) on Feb 04, 2006 at 04:00 UTC

    Class::Trait is a flexible and powerful way to do what I think you really want to do instead of how Java does this.

    Update: typo in module name fixed.

      Not to nit-pick, but it is Class::Trait actually. (which is only important because search.cpan.org's search engine being the way it is, Class::Trait does not come up on the first page of a search for Class::Traits).

      Aside from the misspelling ;) chromatic is absolutely correct. The following code will do what you are asking for:

      # in FooInterface.pm package FooInterface; use Class::Trait 'base'; our @REQUIRES = qw(foo bar baz);
      Then in the class you want to conform to the FooInferface, you can do this:
      package Foo; use Class::Trait 'FooInterface'; sub foo {} sub bar {} # missing &baz
      And you will get this at (sorta) compile time.
      stevan% perl test.pl Requirement (baz) for FooInterface not in Foo at test.pl line 0 INIT failed--call queue aborted.
      I say "sorta" compile time because it is actually during the INIT phase of the compilation cycle. Which is problematic if you are running under mod_perl, or loading the Foo class with require. Both of these limitations (and the appropriate workarounds) are documented in the Class::Trait POD.

      -stvn
Re: Java Style Interface Objects?
by Tuppence (Pilgrim) on Feb 03, 2006 at 22:48 UTC

    If dieing at runtime isn't good enough for you, I would assume you do not have tests to catch your errors before running your code 'in the wild'.

    While it would be a much better idea to integrate a test suite into your development, I believe it will be possible to get the results you want without taking that step.

    You might want to try something like this (untested) code.

    package My::Base; use strict; sub new { my $class = shift; die "Override must_be_overridden" if ($class->can('must_be_overridden') == \&must_be_overridden); } sub must_be_overridden { die "Cannae call this method - Override me!"; }

    Please note that as this uses can it will not work well if you are using AUTOLOADER

Re: Java Style Interface Objects?
by duckyd (Hermit) on Feb 04, 2006 at 00:56 UTC
    Maybe the folllowing will help you get on the right path. It would be nice if you didn't have to call enforce_interface in all the child classes...
    use strict; use warnings; my $bar = Bar->new; #------------------------ package Foo; # implement these in the child class # sub bing { } # sub bang { } # sub bong { } sub enforce_interface { my $self = shift; for ( qw/bing bang bong/ ){ # require these methods die "$_ not implemented" unless $self->can( $_ ); } } 1; #------------------------ package Bar; sub new { return bless {}; } # don't implement bing sub bang { } sub bong { } use base qw/Foo/; __PACKAGE__->enforce_interface; 1;
    and when run:
    bing not implemented at test.pl line 17.