2.8.  Classes and methods

[Warning]Text not verified for kermeta 2

2.8.1. Classes

As introduced in the "Hello world" example (see Section 1.2.1, “ First Program ” ), Kermeta is an object-oriented language. Kermeta provides all MOF concepts like properties, attributes, package. In addition, it provides a body to operations and derived properties.

Classes and abstract classes can be defined in a Java-like way. Class definition must be placed into brackets as it is in the following example.

// an empty simple class
class myFirstClass 
{

}

// a simple abstract class
abstract class MyAbstractClass
{

}

Additionally, for better code robustness, and more user-friendly programming, classes can use the genericity mechanisms (see Section 2.10, “ Genericity ” for more information).

// This is a parametric class
class A<G> {
}

// This is the type variable binding : G is binded with Integer
var a : A<Integer>
a := A<Integer>.new

There are some limitations in regards to Java. For example, you cannot define nested classes in Kermeta. Kermeta offers the same structural concepts than MOF language.

2.8.2. Defining operations

Kermeta provides a way to add operational (action) semantics to your metamodels. For that, you can define operations with their body, as in a classical programming language. You can also specify abstract operations (they have no body). Kermeta requires that every class that contains an abstract operation must be declared as an abstract class.

In the following example, we define some operations, based on a visitor pattern implementation

class Inventory 
{
    operation visitLibrary(l : Library) is
    do
        writeln("Inventory : ");
        l.books.each(b : Book | b.accept(self))
        writeln("----")
    end

    operation visitBook(b : Book) is
    do
        stdio.write("Book : ", b.title, " by ")
        b.authors.each(a : Author | a.accept(self))
    end

    operation visitAuthor(a : Author) is 
    do
        stdio.write(a.name, " ", a.lastName)
    end
}

class Library {
    // ...
    operation accept(visitor : Inventory) is
    do
        visitor.visitLibrary(self)
    end
}

class Book
{
    // ...
    operation accept(visitor : Inventory) is
    do
        visitor.visitBook(self)
    end
}

class Author
{
    // ...
    operation accept(visitor : Inventory) is
    do
        visitor.visitAuthor(self)
    end
}

In this small example we define an Inventory class which can go over the library structure and print books informations. For that, we apply the visitor GoF pattern's on the library structure defining an accept method in every library structures.

2.8.2.1. Result

The special variable result is used to store the value that will be returned by the operation.

operation getName() : String is
    do
        result := self.name
    end
[Note]Note

This is different of the return in java, since it doesn't end the block. Other instructions can be used after the result assignment.

2.8.2.2. Operations as main entry point.

When you run a Kermeta program you'll start from an operation. The main operation that you want to run can have any number of parameters, whose types must be only and only String. For operations that are not intended to be run, you can put any type of parameters.

Technically, if you use Kermeta inside Eclipse, when you will ask it to run your operation, the interpreter will implicitely instanciate the class containing this operation, and then will call this operation.

Example : 3 different kinds of "runnable" operations

class A{
    operation main0() is do
        // do something
    end
    operation main1( arg1 : String) is do
        // do something with 1rst argument
    end
    operation main3( arg1 : String, arg2 : String) is do
        // do something with 1st and 2nd arguments
    end
}
// If you ask to launch main0, Kermeta interpereter will create an instance of A and will run main0 on it.

2.8.3. Initializing classes

Kermeta doesn't use constructors.

However in some situation, you may need to provide some initialization operation. The best approach is simply to declare an operation which will return self after having done the initialization work.

class A
{
	attribute name : String
	operation initialize(name : String) : A is do
		self.name := name
	end
}        
        
// now you can use it easily in one line
	var aA : A init A.new.initialize("hello")
        

2.8.4. Rationale

Here are some explanation about some design choice of Kermeta classes.

2.8.4.1. No constructor

This is because of the compatibility with EMOF and Ecore. In those metalanguages, the user must always be able to create object (except if they are declared abstract), it cannot rely on a action language (since they don't define one). In addition, we want that all Meta tool be able to create the object the same way, so, as Ecore doesn't provide constructor with its editors, then neither does Kermeta.

However, you can easily add an init or myInitialize operation that will take as many parameters as you want. Even better, you can use a Builder or a Factory design pattern.

aspect class ClassFromMyEcore {
	 
	operation init(myParam : String) : ClassFromMyEcore is do
		// assign myParam to some attribute 
		self.name := myParam
		// return self for easier use
		result := self
	end
	// now you can create and initialize this class in one line
	// var foo : ClassFromMyEcore init ClassFromMyEcore.new.init("foo")	
}
          

2.8.4.2. No operation overloading

In order to simplify multiple inheritance management, Kermeta allows only one operation or property with the same name in a given class.