Enactor Script Methods
Enactor Script lets you define methods using familiar Java syntax, with the option to leave parameter and return types unspecified for more flexible, dynamically typed behaviour.
Defining Methods
A method can be declared exactly as it would be in Java, with typed parameters and a typed return value:
int addTwoNumbers( int a, int b ) {
return a + b;
}
It is called the same way any Java method would be:
sum = addTwoNumbers( 5, 7 );
Dynamic Typing in Methods
Parameter and return types can be left out entirely. When they are, Enactor Script works out the appropriate types at call time and tries to "do the right thing":
add( a, b ) {
return a + b;
}
Because the types are resolved per call, the same method can behave differently depending on what it is given:
foo = add(1, 2);
print( foo ); // 3
foo = add("Oh", " baby");
print( foo ); // Oh baby
A method with no declared return type can still return any type of object, or return nothing at all. If it never hits an explicit return statement and its return type is not void, the value of the last statement or expression evaluated in the method body is used as the return value.
Method Modifiers
The standard Java method modifiers - private, protected, public, synchronized, final, native, abstract, static - are all accepted. Of these, only synchronized currently has any effect; the rest are accepted but ignored. A throws clause is checked for validity but is not enforced at runtime.
Synchronized methods synchronize on their enclosing scope, so two synchronized methods defined together behave as if they belonged to the same class:
// foo() and bar() are synchronized as if they were in a common class
synchronized foo() { }
synchronized bar() { }
Scoping of Variables and Methods
A method can see the variables and other methods defined in its enclosing scope, much like an instance method in a Java class can see the fields and methods of that class:
a = 1;
anotherMethod() { ... }
foo() {
print( a );
a = a+1;
anotherMethod();
}
// invoke foo()
foo(); // prints 1
print( a ); // prints 2
A typed variable is never visible outside the scope in which it was declared. An untyped variable, on the other hand, resolves outward through enclosing scopes when read - but as soon as it is assigned inside a method without already existing in an enclosing scope, it becomes local to that method:
a = 1;
foo() {
a = a + 1; // a is defined in the parent scope, so this updates it
b = 3; // undefined anywhere else, so this defaults to local scope
int c = 4; // declared local scope
}
// invoke foo()
foo();
print( a ); // prints 2
print( b ); // ERROR! b undefined
print( c ); // ERROR! c undefined
Forcing a Local, Untyped Variable
Sometimes you want an untyped variable that is local to the method even though a variable of the same name already exists further out - so it shadows the outer one instead of updating it. There are two ways to do this: the var keyword, or the this. qualifier.
foo() {
var a = 1;
}
foo();
print( a ); // ERROR! a is undefined!
var is a special type in Enactor Script that marks a variable as explicitly local and untyped. A variable declared with var defaults to null until it is assigned.
The this. qualifier achieves the same result:
foo() {
this.a = 1;
}
foo();
print( a ); // ERROR! a is undefined!
Scope Modifier: super
Inside a method, the super keyword lets you explicitly reach into the enclosing (parent) scope when looking up a variable or method - useful when a local variable is shadowing one from further out:
int a = 42;
foo() {
int a = 97;
print( a );
print( super.a );
}
foo(); // prints 97, 42
super tells Enactor Script's scope resolution to start its search for the name in the parent scope rather than the current one, which is how you reach an outer-scope value that would otherwise be shadowed.