Apache Jakarta and Beyond: A Java Programmeramp;#039;s Introduction [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

Apache Jakarta and Beyond: A Java Programmeramp;#039;s Introduction [Electronic resources] - نسخه متنی

Larne Pekowsky

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید







16.3. Functions


BeanShell allows arbitrary code to be grouped together into functions, which are something like Java methods. Here is a simple definition and use:


bsh % addThree(i) {i+3;}
bsh % print(addThree(4));
7

Note that neither the return type nor the type of the argument needs to be specified, which is consistent with the way variables work. This allows the creation of functions that take different types of arguments; for example, it is legal to call addThree("The number is "), which will return The number is 3. Arguments can be given types if desired. Note also that the return keyword is not needed, although it is allowed.

Functions can also declare variables, contain iteration, use Java classes, and anything else that can be done at the top level. For example, a function that computes the factorial of a number could be defined as follows:


bsh % fact(n) {
int total = 1;
for(int i=n;i>1;i--) {
total = total*i;
}
total;
}
bsh % print(fact(4));
24

The scoping of variables works as expected; although total is returned from fact(), the variable itself is not defined outside the definition of fact(), as shown by the following error:


bsh % print(total);
// Error: EvalError: Undefined argument: total
: at Line: 12 : in file: <unknown file> : ( total )

So far, this is all very much like Java, but there is much more to BeanShell functions than meets the eye. Consider what must happen when a function like fact() is invoked. Because nothing is compiled, BeanShell doesn't "know" that the first thing it will do is create a variable called total and later a variable called i. Each invocation of fact() behaves exactly as if the user were typing the definition for the first time, so these variables are created as they are encountered in the definition of the function. This implies that each method invocation must have someplace to store variables as they are created; such a construct is called a

namespace.

The same is true at the top level; BeanShell does not know what variables or functions will be defined during a session, so a top-level namespace is also needed. Although it would be possible to have one kind of namespace for the top level and another for function invocations, BeanShell does something much more clever and makes them

identical. One of the implications of this is that it is possible to define functions within functions:


bsh % addSquares(x,y) {
square(a) {a*a;}
square(x) + square(y);
}
bsh % print(addSquares(2,3));
13

Here square() is only available within the body of addSquares(). Attempting to use it outside that scope would result in the same kind of error that arose from attempting to access total outside of fact().

Even more amazing than the existence of multiple namespaces is the ability to access and manipulate these namespaces. In any context the special variable this refers to the current namespace, although in this context the namespace is more properly called a

closure. Values within a closure can be both obtained and set with the usual Java dot notation. In any context, including the top level, this.variable_name and variable_name mean the same thing.


bsh % i=44;
bsh % print(this.i);
44
bsh % this.i=76;
bsh % print(i);
76

The real utility of this lies in the ability to make one namespace available from another. The easiest way to do this is to return this from a function call.


bsh % makeClosure() {
int value = 88;
int getValuePlusOne() {return value + 1;}
return this;
}

makeClosure now returns a new closure whose values can be manipulated from the top level.


bsh % c1=makeClosure();
bsh % print(c1.value);
88
bsh % print(c1.getValuePlusOne());
89
bsh % c1.value = 256;
bsh % print(c1.getValuePlusOne());
257

The reason for the name this should now be evident: Its use enables the creation of closures that act much like objects. A function that returns such a closure acts as a constructor. This similarity is further highlighted by noting that each call to makeClosure() will create a brand new namespace, just as a call to new creates a new instance of a class.


bsh % c2=makeClosure();
bsh % print(c2.value);
88
bsh % print(c1.value);
256

Unlike classes, it is possible to add new values to a closure dynamically after it has been created:


bsh % c1.newValue=11;
bsh % print(c1.newValue);
11
bsh % print(c2.newValue);
// Error: EvalError: Undefined argument: c2 .newValue
: at Line: 23 : in file: <unknown file> : ( c2 .newValue )

It is occasionally useful to do the reverse: pass the global namespace into a function so that the function may set global values:


bsh % setSquare(ns,n) {
ns.lastSquare=n*n;
}
bsh % setSquare(this,4);
bsh % print(lastSquare);
16


/ 207