Active annotations allow developers to participate in the translation process of SARL source code to Java. An active annotation is just an annotation that is processed by a specific annotation processor during the compilation of a SARL program. Depending on the meaning of the active annotation, the generated Java code could be completed by the annotation processor.
SARL comes with ready-to-use active annotations for common code patterns. The following sections describe there annotations.
If you want to add getter and or setter methods for your fields, @Accessors
is your friend.
This annotation can be applied to either object-oriented types and several agent-oriented types.
The agent-oriented types in which you could uses the @Accessors
annotation are the agents,
the behaviors and the skills.
To uses this annotation, you have to import it as:
import org.eclipse.xtend.lib.annotations.Accessors
Let’s a basic example.
@Accessors var name : String
It is compiled to the code:
private var name : String
public def getName : String {
this.name
}
public def setName(name : String) : void {
this.name = name
}
By default, a public getter and a public setter method is created. The @Accessors
can be configured to tell
that you only want one or the other, and to change the visibility. This is done by passing one or more values of
type AccessorType
for representing the visibility categories
as parameters to the annotation: PUBLIC_GETTER
, PROTECTED_GETTER
, PACKAGE_GETTER
,
PRIVATE_GETTER
, PUBLIC_SETTER
, PROTECTED_SETTER
, PACKAGE_SETTER
,
PRIVATE_SETTER
, NONE
.
You can also use the annotation on class level to do the same for all fields.
Here is a more complex example, that shows how it works:
@Accessors class Person {
var name : String
var firstName : String
@Accessors(PUBLIC_GETTER, PROTECTED_SETTER) var age : int
@Accessors(NONE) var internalField : String
}
It is compiled to the code:
class Person {
private var name : String
private var firstName : String
private var age : int
private var internalField : String
public def getName : String {
this.name
}
public def setName(name : String) : void {
this.name = name
}
public def getFirstName : String {
this.firstName
}
public def setFirstName(firstName : String) : void {
this.firstName = firstName
}
public def getAge : int {
this.age
}
protected def setAge(age : int) : void {
this.age = age
}
}
The annotation @Data
will turn an annotated class into a value object class. A class annotated with
@Data
is processed according to the following rules:
val
,This annotation can be applied to object-oriented types. The agent-oriented types cannot be annotated.
Example:
import org.eclipse.xtend.lib.annotations.Data
@Data class Person {
val firstName : String
val lastName : String
static def main(args : String*) {
val p = new Person(args.get(0), args.get(1))
println(p.getFirstName + ' ' + p.lastName)
}
}
The @Delegate
annotation automatically generates delegate methods for all interfaces shared between the delegate
and the currently implemented class. You can optionally restrict it to explicitly stated interfaces.
This annotation can be applied to object-oriented types. The agent-oriented types cannot be annotated.
Let’s start with a basic example:
import org.eclipse.xtend.lib.annotations.Delegate
interface SomeInterface {
def function(param : String) : int
}
interface SubTypeOfSomeInterface extends SomeInterface {
def anotherFunction
}
class MyClass implements SomeInterface {
// generates all methods of SomeInterface and delegates to this field
@Delegate var myDelegate : SubTypeOfSomeInterface
}
The previous code is equivalent to:
class MyClass implements SomeInterface {
var myDelegate : SubTypeOfSomeInterface
def function(param : String) : int {
return this.myDelegate.function(param)
}
}
It is not only possible to delegate to fields, but also to methods so you could lazily create the delegate object or use a different one each time.
class MyClass implements SomeInterface {
@Delegate def provideDelegate : SomeInterface {
return new MyDelegate
}
}
The previous code is equivalent to:
class MyClass implements SomeInterface {
def function(param : String) : int {
return provideDelegate().function(param)
}
def provideDelegate : SomeInterface {
return new MyDelegate
}
}
If you use a method, additional parameters could be declared, that will tell you about the method that should be invoked:
Let’s the following example:
class MyClass implements SomeInterface {
@Delegate def provideDelegate(methodName : String, parameterTypes : Class<?>[], arguments : Object[]) : SomeInterface {
return new MyDelegate
}
}
The previous code is equivalent to:
class MyClass implements SomeInterface {
def function(param : String) : int {
return provideDelegate(
"function",
#[typeof(String)],
#[param]).function(param)
}
def provideDelegate(methodName : String, parameterTypes : Class<?>[], arguments : Object[]) : SomeInterface {
return new MyDelegate
}
}
The @Inline
annotation is related to the feature of the SARL compiler that suggests that the compiler substitutes the code within the annotation definition in place of each call to that function.
In theory, using @Inline
functions can make your program faster because they eliminate the overhead associated with function calls.
From background point-of-view, calling a function requires pushing the return address on the stack, pushing arguments onto the stack, jumping to the function body, and then executing a return instruction when the function finishes.
This process is eliminated by inlining the function.
The compiler also has different opportunities to optimize functions expanded inline versus those that aren’t.
A tradeoff of inline functions is that the overall size of your program can increase.
The following code is an example of the usage of the @Inline
annotation.
The defined function is computing the double of the multiplication of the two arguments.
The annotation specifies the Java expression that will be used as a replacement in the Java code for the function call to mul
.
import org.eclipse.xtext.xbase.lib.Inline
class MyClass {
@Inline("((﹩1) * (﹩2) * 2)")
def mul(a : int, b : int) : int {
a * b * 2
}
}
As it is illustrated before, the value of the annotation represents the Java code replacement.
This inline format string contains valid Java code with several placeholders like $1
, $2
, etc.
The number after the dollar sign corresponds to the index of the information that is used during the replacement process.
When the inlined function has 1..n
parameters, then $1
to $n
are used to represent there parameters, and the subsequent m
values corresponds to the m
types specified with the imported
parameter of the @Inline
annotation.
The next index $n+m+1
} can be used to insert all type parameters of the original declaration.
And, finally the last indices refer to the upper bound substitute of the type parameters individually.
In the case a not-static function, two special numbers are reserved: $0
is replaced by the object expression for which the inlined function was called, followed by a .
character; and $-1
is replaced by the object expression for which the inlined function was called, without a following .
character.
Let the following example for illustrating the values of the @Inline
placeholders.
@Inline(value="......", imported = typeof(BigDecimal))
def myMethod(p1 : String, p2 : String) : void with T1, T2 extends Byte
The call to the previously defined function is:
var obj : MyInterface
obj.<Integer, Byte>myMethod("abc", "def")
The following table provides a synthetic view of the @Inline
placeholders.
N. | Description | In the example |
---|---|---|
-1 | The calling receiver of the function without final dot character | obj |
0 | The calling receiver of the function with final dot character | obj. |
[1..n] | n parameters of the function |
"abc" , "def" |
(n..k] | m imported types (k=n+1+m) |
typeof(BigDecimal) |
k+1 | all of the p generic types of the function |
T1, T2 extends Byte |
(k+1..i] | Upper bound of the type parameters (i=k+p+2) | Object , Byte |
The @Inline
annotation has different arguments:
Argument | Description |
---|---|
value |
The inline format string |
imported |
Types that should be imported to inline the operation |
statementExpression |
Whether the inlined expression is a statement expression in the Java code |
constantExpression |
Whether the compiled Java is a constant expression operator, i.e. $0 is not automatically written as a prefix of the provided inline format string |
The @NoEqualityTestFunctionsGeneration
annotation disables the generation the equality test functions, i.e. equals()
and hashCode()
from
the field declarations.
By default, the SARL compiler generates the equality test functions from the type’s fields. In several cases, this automatic
behavior should be avoiding because the standard equality test that is provided by the Java run-time environment should be used.
In this case, @NoEqualityTestFunctionsGeneration
annotation may be used to mark a type or a field for being excluded of the equality test generation.
The annotation may mark a type, as in the following example. In this case, no equality test function is generated within the marked type and all its subtypes.
import io.sarl.lang.core.annotation.NoEqualityTestFunctionsGeneration
@NoEqualityTestFunctionsGeneration
class MyClass {
var field1 : int
var field2 : String
}
The annotation may mark a specific field in order to exclude it from the equality test generation.
In the following example, the field2
field is marked with the annotation. Consequently, it
is not included within the equality test within the equals()
function, and the hash code replied
by the hashCode()
function does not include the hash code of the field2
field.
class MyClass {
var field1 : int
@NoEqualityTestFunctionsGeneration
var field2 : String
}
The @ToString
annotation enables to generate the function that replies the string representation
of an object, a.k.a. as the toString()
function in a Java program.
All non-static fields of the annotated class, and all of its superclasses are used for generating the toString()
function.
This annotation can be applied to object-oriented types. The agent-oriented types cannot be annotated.
Let’s a basic example:
import org.eclipse.xtend.lib.annotations.ToString
@ToString
class MyClass {
var field1 : int
var field2 : String
}
The previous code is equivalent to:
class MyClass {
var field1 : int
var field2 : String
def toString() : String {
var buffer = new ToStringBuilder(this)
buffer.add("field1", this.field1);
buffer.add("field2", this.field2);
return buffer.toString
}
}
For brevity there are options to the annotation to hide field names, skip fields with null values and print everything on one line.
This documentation is inspired by the documentations from the Xtext and Xtend projects.
Copyright © 2014-2024 SARL.io, the Original Authors and Main Authors.
Documentation text and medias are licensed under the Creative Common CC-BY-SA-4.0; you may not use this file except in compliance with CC-BY-SA-4.0. You may obtain a copy of CC-BY-4.0.
Examples of SARL code are licensed under the Apache License, Version 2.0; you may not use this file except in compliance with the Apache License. You may obtain a copy of the Apache License.
You are free to reproduce the content of this page on copyleft websites such as Wikipedia.
Generated with the translator docs.generator 0.14.0.