📄 ch5.htm
字号:
you would use the following statement:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">@EXPORT(declareMain, closeMain);</FONT></TT>
</BLOCKQUOTE>
<P>
Inheritance in a Perl class is accomplished via the <TT><FONT FACE="Courier">@ISA</FONT></TT>
array. The <TT><FONT FACE="Courier">@ISA</FONT></TT> array does
not have to be defined in every package; however, when it is defined,
Perl treats it as a special array of directory names. This is
akin to the <TT><FONT FACE="Courier">@Inc</FONT></TT> array where
directories are searched for files to include. In the case of
the <TT><FONT FACE="Courier">@ISA</FONT></TT> array, the paths
define the classes (packages) and where to look for other class
packages, if a method is not found in the current package. Thus,
the <TT><FONT FACE="Courier">@ISA</FONT></TT> array contains the
names of the base classes from which the current class inherits.
The search is done in the order in which the classes are listed
in the <TT><FONT FACE="Courier">@ISA</FONT></TT> arrays.
<P>
All methods called by a class do have to belong to the same class
or to the base classes defined in the <TT><FONT FACE="Courier">@ISA</FONT></TT>
array. If a method isn't found in <TT><FONT FACE="Courier">@ISA</FONT></TT>
array, Perl looks for an <TT><FONT FACE="Courier">AUTOLOAD()</FONT></TT>
routine. This routine is defined as <TT><FONT FACE="Courier">sub</FONT></TT>
in the current package and is optional. To use the <TT><FONT FACE="Courier">AUTOLOAD</FONT></TT>
function, you have to use the <TT><FONT FACE="Courier">autoload.pm</FONT></TT>
package with the <TT><FONT FACE="Courier">use Autoload</FONT></TT>;
statement. The <TT><FONT FACE="Courier">AUTOLOAD</FONT></TT> function
tries to load the called function from the installed Perl libraries.
If the <TT><FONT FACE="Courier">AUTOLOAD</FONT></TT> call also
fails, Perl makes one final try at the <TT><FONT FACE="Courier">UNIVERSAL</FONT></TT>
class, which is the catch-all for all methods not defined elsewhere.
Perl will generate an error about unresolved functions if this
step also fails.
<P>
Here are some simple rules when exporting methods. First, export
only those functions that you have to. Do not export every function
in your module because you will be increasing the likelihood of
conflicts with a program that is using your module. Use the <TT><FONT FACE="Courier">@EXPORT_OK</FONT></TT>
array instead of the <TT><FONT FACE="Courier">@EXPORT</FONT></TT>
array if you feel that the names of your methods may clash with
those in an application. Choosing long, descriptive names for
functions may help eliminate problems with synonymous variable
names.
<P>
Second, if you are going to have multiple versions of your module,
consider setting a variable called <TT><FONT FACE="Courier">$VERSION</FONT></TT>
in your module to a numeric string; for example, <TT><FONT FACE="Courier">"2.11"</FONT></TT>
or something. This version number will be exported for you automatically
and can be used with the <TT><FONT FACE="Courier">require</FONT></TT>
statement. Remember to use two digits for all integers in the
version numbers because <TT><FONT FACE="Courier">"1.10"</FONT></TT>
is interpreted lower than <TT><FONT FACE="Courier">"1.9"</FONT></TT>
but higher than <TT><FONT FACE="Courier">"1.09"</FONT></TT>.
You will see some modules or programs with a statement of the
following form:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">require 5.001; </FONT></TT>
</BLOCKQUOTE>
<P>
The statement above indicates that Perl version 5.001 or greater
is required. The same analogy can be used for your module with
a call to a function called<TT><FONT FACE="Courier"> require_version</FONT></TT>
of the following form:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$moduleName->require_version($value);</FONT></TT>
</BLOCKQUOTE>
<P>
A returned value of <TT><FONT FACE="Courier">true</FONT></TT>
will indicate that it's okay to proceed. A returned value of <TT><FONT FACE="Courier">false</FONT></TT>
will indicate that the version number of the module is less than
what is specified in the <TT><FONT FACE="Courier">$value</FONT></TT>.
<H2><A NAME="InvokingMethods"><FONT SIZE=5 COLOR=#FF0000>Invoking
Methods</FONT></A></H2>
<P>
There are two ways to invoke a method for an object: one via a
reference to an object (virtual) and the other via explicitly
referring to the class name (static). A method has to be exported
for you to be able to call it. Let's add a few more methods to
the <TT><FONT FACE="Courier">Cocoa</FONT></TT> class to get the
file to look like the one shown in Listing 5.10.
<HR>
<BLOCKQUOTE>
<B>Listing 5.10. Adding methods to the </B><TT><B><FONT FACE="Courier">Cocoa</FONT></B></TT><B>
class.<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier"> 1 package Cocoa;<BR>
2 require Exporter;<BR>
3 <BR>
4 @ISA = qw(Exporter);<BR>
5 @EXPORT = qw(setImports,
declareMain, closeMain);<BR>
6 <BR>
7 #<BR>
8 # This routine creates the references for imports in Java
functions<BR>
9 #<BR>
10 sub setImports{<BR>
11 my $class = shift @_;<BR>
12 my @names = @_;<BR>
13 <BR>
14 foreach (@names) {<BR>
15 print "import "
. $_ . ";\n";<BR>
16 }<BR>
17 }<BR>
18 <BR>
19 #<BR>
20 # This routine declares the main function in a Java script
<BR>
21 #<BR>
22 sub declareMain{<BR>
23 my $class = shift @_;<BR>
24 my ( $name, $extends, $implements)
= @_;<BR>
25 <BR>
26 print "\n public class
$name";<BR>
27 if ($extends) {<BR>
28
print " extends " . $extends;<BR>
29 }<BR>
30 if ($implements) {<BR>
31
print " implements " . $implements;<BR>
32 }<BR>
33 print " { \n";<BR>
34 }<BR>
35 <BR>
36 #<BR>
37 # This routine declares the main function in a Java script
<BR>
38 #<BR>
39 sub closeMain{<BR>
40 print "} \n";<BR>
41 }<BR>
42 <BR>
43 #<BR>
44 # This subroutine creates the header for the file.
<BR>
45 #<BR>
46 sub new {<BR>
47 my $this = {};<BR>
48 print "\n /* \n ** Created by
Cocoa.pm \n ** Use at own risk \n */ \n";<BR>
49 bless $this;<BR>
50 return $this;<BR>
51 }<BR>
52 <BR>
53 1;</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
Now let's write a simple Perl script to use the methods for this
class. Because you can only start and close the header, let's
see how the code for a script to create a skeleton Java applet
source looks. (See Listing 5.11.)
<HR>
<BLOCKQUOTE>
<B>Listing 5.11. Using the methods just added in Listing 5.10.
<BR>
</B>
</BLOCKQUOTE>
<BLOCKQUOTE>
<TT><FONT FACE="Courier">1 #!/usr/bin/perl<BR>
2 <BR>
3 use Cocoa;<BR>
4 <BR>
5 $cup = new Cocoa;<BR>
6 <BR>
7 $cup->setImports( 'java.io.InputStream', 'java.net.*');<BR>
8 $cup->declareMain( "Msg" , "java.applet.Applet",
"Runnable");<BR>
9 $cup->closeMain();</FONT></TT>
</BLOCKQUOTE>
<HR>
<P>
What we are doing in this script is generating code for a Java
applet called <TT><FONT FACE="Courier">Msg</FONT></TT>, which
extends the <TT><FONT FACE="Courier">java.applet.Applet</FONT></TT>
applet and implements functions that can be run. The function
is called with a function <TT><FONT FACE="Courier">$cup->...</FONT></TT>
call. Lines 7 through 9 could be rewritten as functions, like
this:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">Cocoa::setImports($cup, 'java.io.InputStream',
'java.net.*');<BR>
Cocoa::declareMain($cup, "Msg" , "java.applet.Applet",
"Runnable");<BR>
Cocoa::closeMain($cup);</FONT></TT>
</BLOCKQUOTE>
<P>
This type of equivalence was shown in a previous section, "Blessing
a Constructor." In both cases, the first parameter is the
reference to the object itself. Running this test script generates
the following output:
<BLOCKQUOTE>
<TT><FONT FACE="Courier"> /*<BR>
** Created by Cocoa.pm<BR>
** Use at own risk<BR>
*/<BR>
import java.io.InputStream;<BR>
import java.net.*;<BR>
<BR>
public class Msg extends java.applet.Applet implements Runnable
{<BR>
}</FONT></TT>
</BLOCKQUOTE>
<P>
There are a couple of points to note when calling the methods.
If you have any arguments to a method, use parentheses if you
are using the method <TT><FONT FACE="Courier">-></FONT></TT>
(also known as indirect). The parentheses are required to include
all the arguments with this statement:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$cup->setImports( 'java.io.InputStream',
'java.net.*');</FONT></TT>
</BLOCKQUOTE>
<P>
However, this statement:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">Cocoa::setImports($cup, 'java.io.InputStream',
'java.net.*');</FONT></TT>
</BLOCKQUOTE>
<P>
can also be rewritten without parentheses.
<BLOCKQUOTE>
<TT><FONT FACE="Courier">Cocoa::setImports $cup, 'java.io.InputStream',
'java.net.*' ;</FONT></TT>
</BLOCKQUOTE>
<P>
The choice is really yours as to how you intend to make your code
readable for other programmers. Use parentheses if you feel that
the code will be more readable.
<H2><A NAME="Overrides"><FONT SIZE=5 COLOR=#FF0000>Overrides</FONT></A>
</H2>
<P>
There are times when you'll want to specify which class' method
to use, such as when the same-named method is specified in two
different classes. For example, if the function <TT><FONT FACE="Courier">grind</FONT></TT>
is defined in both <TT><FONT FACE="Courier">Espresso</FONT></TT>
and <TT><FONT FACE="Courier">Qava</FONT></TT> classes, you can
specify which class' function to use with the use of the <TT><FONT FACE="Courier">::</FONT></TT>
operator. These two calls:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$mess = Espresso::grind("whole","lotta","bags");
<BR>
Espresso::grind($mess, "whole","lotta","bags");</FONT></TT>
</BLOCKQUOTE>
<P>
use the call in <TT><FONT FACE="Courier">Espresso</FONT></TT>,
whereas the following calls use the <TT><FONT FACE="Courier">grind()</FONT></TT>
function in the <TT><FONT FACE="Courier">Qava</FONT></TT> class:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$mess = Qava::grind("whole","lotta","bags");
<BR>
Qava::grind($mess, "whole","lotta","bags");</FONT></TT>
</BLOCKQUOTE>
<P>
Sometimes you want to call a method based on some action that
the program you are writing has already taken. In other words,
you want to use the <TT><FONT FACE="Courier">Qava</FONT></TT>
method for a certain condition and the <TT><FONT FACE="Courier">Espresso</FONT></TT>
method for another. In this case, you can use symbolic references
to make the call to the required function. This is illustrated
in the following example:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">$method = $local ? "Qava::"
: "Espresso::";<BR>
$cup->{$method}grind(@args);</FONT></TT>
</BLOCKQUOTE>
<H2><A NAME="Destructors"><FONT SIZE=5 COLOR=#FF0000>Destructors</FONT></A>
</H2>
<P>
Perl tracks the number of links to objects. When the last reference
to an object goes away, the object is automatically destroyed.
This destruction of an object may occur after your code stops
and the script is about to exit. For global variables, the destruction
happens after the last line in your code executes.
<P>
If you want to capture control just before the object is freed,
you can define a <TT><FONT FACE="Courier">DESTROY()</FONT></TT>
method in your class. Note the use of all capitals in the name.
The <TT><FONT FACE="Courier">DESTROY()</FONT></TT> method is called
just before the object is released, allowing you to do any cleanup.
The <TT><FONT FACE="Courier">DESTROY()</FONT></TT> function does
not call other <TT><FONT FACE="Courier">DESTROY()</FONT></TT>
functions. Perl doesn't do nested destruction for you. If your
constructor reblessed a reference from one of your base classes,
your <TT><FONT FACE="Courier">DESTROY()</FONT></TT> may need to
call <TT><FONT FACE="Courier">DESTROY()</FONT></TT> for any base
classes. All object references that are contained in a given object
are freed and destroyed automatically when the current object
is freed.
<P>
Normally, you don't have to define a <TT><FONT FACE="Courier">DESTROY</FONT></TT>
function. However, when you do need it, its form is as follows:
<BLOCKQUOTE>
<TT><FONT FACE="Courier">sub DESTROY {<BR>
#<BR>
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -