|Perl: the Markov chain saw|
Re: difference between OOPs in Perl and javaby ELISHEVA (Prior)
|on Jan 21, 2011 at 08:21 UTC||Need Help??|
This feels just a bit like an exam question to me. The question feels just a little bit too neat and the more I wrote below, the more I felt that way. Still, I think it is important question for those who care about OOP, so I'm providing, with reservation, an answer below. I don't like giving people answers to questions that are meant to be a test of their own synthesis of class materials rather than a test of their ability to "find it on the web".
Scoping (public/private/protected): In Java encapsulation is enforced by the compiler. It is impossible to write code that accidentally accesses variables meant to be private. This increases your upfront work since you have to explicitly declare the access level of any public or protected member (fields and methods with no explicit declaration are private to a package). It decreases your testing work, because you don't need to worry about constructing tests to see if consumer packages are obeying the privacy rules.
Subclass access to superclass data: In Java you can access protected variables inherited from the parent without qualification. In Perl, any variable not in the current package, must be qualified. To access a data member of a parent class, without qualification you have to explicitly assign it to a variable within the current package. (Note: you could arrange to auto-export protected variables by writing a special import routine that checked the "@ISA" variable for the package calling "use XXX" or import(), but this isn't out of the box.)
Java also protects you from name collision. In Perl, you must use and publicize some sort of naming convention in order to insure that subclasses don't clobber the variables of superclasses. In Java, names are scoped to their package. If the superclass and subclass have the same named field, the subclass field hides the superclass field, but you can always access the superclass field, by using super.myvar instead of this.myvar.
Perl supports multiple inheritance, provides two built-in strategies for prioritizing implementations from different superclasses, and also gives you a way to roll your own, if you wish.
Java is much more restrictive. It is often said to support only single inheritance, but it would be more accurate to say that it supports a very restricted form of multiple inheritance.
In Java you can only declare a single class as superclass. However, you can give a class as many interfaces as you wish. The same things get inherited (classes, methods, data) from both the superclass and the interface. The only difference is in what an interface can contain.
Interfaces can only define abstract methods, that is, object methods withut implementations. So you can inherit object method prototypes from an interface but not object method implementations. You can only inherit object method implemetnations from a superclass.
However, that does not mean you can't inherit implementations from an interface - you just have to define them in a member class. If an interface wants to provide inheritable implementations it usually does it this way. Then the subclass defines boiler plate code that implements each abstract object method inherited from the interface by wrapping one of the provided member class methods.
You can even go one further and implement the methods by sending the call to a proxy object which then identifies the interfaces and available methods via reflections and chooses which interface's implementation to use. Thus, even in Java you can roll your own inheritance scheme if you really, really want to.
The main difference as regards inheritance is really in how you write the code. In Perl, multiple inheritance of implementations is out-of-the-box. In Java, it is not and you have to do a lot of work and have a fair amount of Java expertise to emulate it.
Both Java and Perl support true polymorphism, but Java gives you more explicit control over what others can do with your class. In Java you can prevent overriding of a method by declaring the method final and you can force subclasses to define a method by declaring it abstract. This makes it possible to do compile time checks on new classes.
Perl doesn't offer any such control. The best you can do to force declaration of a method is to have the superclass method die or spit out warnings. However, these warnings will only take effect if a running program tries to use a method that was not overridden and should have been. There is no compile time check. As for declaring methods final, I'm not sure how you would do it without doing something really fancy to intercept all additions to the symbol table. (Can that even be done?).
The upside of Perl's approach to polymorphism is that it is easier to patch bad code without actually editing it. In Java if a method is declared final by an over-aggressive OOP cop, and you are an API consumer you are stuck unless you can rewrite that class. In Perl, you are never stuck that way.
The downside is that if that final method is really important to the class's longterm health and right use, you have to write a conformance test suite for subclasses. In Java the compiler will do all the work for you.
Some final thoughts
The biggest difference however between Java and Perl isn't in the three topics you mentioned (Encapsulation, Inheritance, and Polymorphism), but rather in something much deeper: the underlying model for bundling functionality and data into a single unit or object.
In Java you are forced into a data centric notion of object. In Perl you can choose between a functional and a data centric object.
In Perl, if all your data is used by one main function, in lieu of defining a package, you can define a closure function that will hold copies of all relevant data plus its own code. This is often much neater and simpler than defining a class and all its apparatus.
In Java, the only way to combine data and functionality in a unit is to declare a class. To be sure, you can define a temporary class with a single method and place it within a method call. Like a closure it grabs copies of the data in the surrounding method. However, you still need to do all the boilerplate code required for a class declaration even though all you want is that one class method.