A class in object oriented programming declares the structure and functionality of an object. Since Sling is an object oriented language by its nature, classes are essential for any Sling program.
Declaration
The most typical way to declare a class is to declare a single class per file. This is accomplished by appending a single colon (":") character at the end of the class declaration line, which indicates that the class will continue from there until the end of file. This is referred to as "file mode", and is done in accordance to the following example:
// Filename: MyClass.sling
class:
// Class members will be here
// The class continues until end of file
Note that no name was specified for the class: In this case, the class is automatically named "MyClass", according to the name of the file. To rename the class, simply rename the source code file. It is, however, possible to explicitly set the name of the class using the "name" modifier:
// Filename: MyClass.sling
class name ThisIsNotMyClass:
// Class members will be here
// The class continues until end of file
The above example declares a class named ThisIsNotMyClass, but saves it in MyClass. This feature is made available for the sake of extraordinary situations, and should not be used in normal cases. The following syntax is also valid, although discouraged:
// Filename: MyClass.sling
class name ThisIsNotMyClass
{
// Class members will be here
// The class continues until the closing brace
}
Note that in the case of inner classes (classes inside other classes), the name of the class is required, and therefore the "name" modifier is also not necessary. The declaration of an inner class is done as follows:
// Filename: MyClass.sling
class:
class ThisIsInnerClass
{
}
The above example declares a class named "MyClass" and inner class "ThisIsInnerClass".
The complete declaration pattern for a class is as follows (note the "*" markings below, indicating that the parameter may appear multiple times):
class [name <name>] [is <basetype>]* [imports <namespace/module>]*
[includes <namespace/module>]* [uses <namespace/module>]*
[depends <namespace/module>]* [public] [private] [nsprivate]
[protected] [fundamental] [static] [extern] [final] [deprecated]
[partial] [abstract] [extends|xt <basetype>]
[implements|mp <basetype>] [isAlso <basetype>]
[basetype|bt <basetype>] [commonParam(paramName as DataType)]
[custom modifiers]
Note that some of the class declaration parameters can also be given as declarations inside the class itself. Specifically, the parameters "depends", "imports", "includes", "basetype" and the various modifiers (public, private, nsprivate, protected, fundamental, static, extern, final, deprecated, partial, abstract and custom modifiers) can be also declared inside the class. Consider the following:
class ThisIsMyClass abstract private is OtherClass is AlsoAnotherClass imports name.space
{
}
The following code is fully equivalent:
class ThisIsMyClass
{
modifier abstract
modifier private
basetype OtherClass
basetype AlsoAnotherClass
import name.space
}
Note that the parameter keywords in the class declaration are descriptive (you read it as "this class imports name.space"), while inside the class the corresponding keywords are imperative ("import name.space"). The following illustrates the equivalence of the keywords:
class SampleClass depends a imports b includes c
{
}
Equivalent declaration:
class SampleClass
{
depend a
import b
include c
}
Inheritance
Base types of a class are usually declared using the "is" modifier as part of the class declaration. The "is" modifier can be used both for extending classes and implementing interfaces: The compiler will automatically detect the correct behavior depending on the type of the entity that is being extended. However, if necessary, the "is" modifier can be replaced explicitly using either "extends" or "implements":
Sample: Inheriting a class
// This is the base class
class FirstClass
{
}
// This class inherits the FirstClass above
class SecondClass is FirstClass
{
}
Sample: Implementing an interface
interface SampleInterface
{
func doSomething
func doSomethingElse(value as int)
}
class Implementation is SampleInterface
{
func doSomething
{
}
func doSomethingElse(value as int)
{
}
}
Sample: Inheriting and implementing simultaneously
// Inherits SecondClass (above) AND implements SampleInterface
class AnotherClass is SampleInterface is SecondClass
{
func doSomething
{
}
func doSomethingElse(value as int)
{
}
}
As mentioned, it is possible to explicitly specify the type of the "is" relationship by using the "extends" (for inheriting a class) or "implements" (for implementing an interface). The following sample is equivalent to the one above:
// Inherits SecondClass (above) AND implements SampleInterface
class AnotherClass implements SampleInterface extends SecondClass
{
func doSomething
{
}
func doSomethingElse(value as int)
{
}
}
The use of "extends" and "implements" is discouraged, however, and the "is" operator should be used unless pressing reasons demand otherwise.
Abstract Classes
An abstract class cannot be instantiated, and is usually marked as abstract because it contains abstract methods (although it is perfectly legal to declare a class abstract even if it has no abstract methods):
class MyAbstractClass abstract
{
}
Abstract methods inside the class must be marked with the "abstract" modifier, and must not contain a method body:
class MyAbstractClass abstract
{
func myAbstractMethod abstract
}
Any class that inherits MyAbstractClass would then need to override the method myAbstractMethod.