Xtend官方文档——第二部分(一)

第二章 语言文档参考(一)

第一节  Java互操作性

XtendJava一样,是一种静态类型的语言。实际上,它完全支持Java的类型系统,包括基本类型,int或者boolean数组以及类路径上所有的Java类、接口、枚举和注等。

完全支持Java泛型:您可以在方法和类上定义类型参数,并将类型参数传递给通用类型,就像Java一样使用。类型系统及其一致性和转换规则按照Java语言规范定义实现。

支持Java类型系统的各个方面确保JavaXtend之间匹配。这意味着XtendJava可以100%互操作。你不必在考虑两种语言的特殊情况。您可以从Java调用Xtend代码,反之亦然,无任何意外或麻烦。如果你知道Java的类型系统,并熟悉Java的通用类型,那么你已经知道Xtend中最复杂的部分。

Xtend-to-Java编译器的默认行为是生成与相应项目中的Java编译器版本兼容性的Java代码。这可以在首选项或Xtend→Compiler页面的项目属性中更改(自2.8起)。根据选择的Java语言版本,Xtend可能会生成不同但等效的代码。例如,如果编译器设置为Java 8,则将lambda表达式转换为Javalambdas,而较低版本则生成Java的匿名类。

类型推断

Java的一个问题是不得不一次又一次写类型签名。这就是为什么这么多人不喜欢静态类型。但实际上这不是静态类型的问题,而是Java的问题。虽然Xtend是静态类型的,就像Java一样,你很少需要写类型,因为它们可以从上下文中推断出来。

考虑以下Java变量声明:

final LinkedList<String> list = new LinkedList<String>();

构造函数调用的类型必须重复编写变量类型声明。在Xtend中,可以从初始化表达式推断出变量类型:

val list = new LinkedList<String>

转换规则

除了Java的自动装箱以外还可将基本类型转换为相应的包装类型(例如int自动转换为整数)时,Xtend还有其他转换规则。

数组自动转换为List<ComponentType>反之亦然。就是说,你可以写下列内容:

def toList(String[] array) {

  val List<String> asList = array

  return asList

}

对数组的后续更改反映在列表中,反之亦然。基本类型的数组将转换为各自的包装类型的列表。

转换也能反过来工作。事实上,所有子类型Iterable都会自动转换为数组。

另一个非常有用的转换适用于lambda表达式。lambda表达式通常是函数过程声明的类型之一。但是,如果期望类型是声明一个抽象方法的接口或1,则lambda表达式将自动转换为该类型。这允许lambda表达式使用许多现有的Java库。有关详细信息,请参阅Lambda表达式

1:关于:Single Abstract Method(SAM)类型是一种约定。Java语义没有function type或者delegate type。但lambda表达式的需要类型怎么办?于是,就安个接口,而接口里方法叫什么名字没关系,只要保证这个接口只有一个方法是抽象、无实现的就可以了,这样lambda表达式的函数体就可以充当这个抽象方法的实现。SAM类型的约定就这么来了。

 

第二节 类和成员

Xtend文件看起来像一个Java文件。它以包声明开始,后跟导入和类定义。这些类实际上直接转换为相应的Java类。类可以有构造函数字段方法和注

这是一个Xtend文件示例:

package com.acme

import java.util.List

class MyClass {

  String name

  

  new(String name) {

    this.name = name

  }

  

  def String first(List<String> elements) {

    elements.get(0)

  }

}

包声明

包声明可以像Java那样。有两个小的可选的区别:

· 可以用^字符转义标识符,以防其与关键字冲突。

· 终止位置分号“;”是可选的。

  package com.acme

导入

类型名称的普通导入等同于从Java的导入。次,可以使用一个^避免任何与关键字冲突的名称。与Java不同,终止分号是可选的。为了更好的可用性和明确的依赖关系,非静态通配符类型导入已被弃用。

Xtend还具有静态导入用于导入静态字段和方法。语义和语法就像Java一样。

Java一样,来自java.lang包的所有类都被隐式导入。

import java.math.BigDecimal

import static java.util.Collections.sort

import static org.junit.Assert.*

静态方法也可以导入为extensions。有关详细信息,请参阅扩展方法部分。

类声明

类声明重用了很多Java的语法,但在某些方面仍然有些不同:所有Xtend类型默认都是public,因为这是常见的情况。Java默认的“package private”可见性Xtend中由更明确的关键字package声明。与Java相反,Xtend支持每个文件多个public顶级类声明。每个Xtend分别被编译为一个顶级Java类。

抽象类使用Java中的abstract修饰符定义。抽象方法

Xtend的继承方法在概念上与Java中的一样。支持类的单一继承以及实现多个接口。Xtend类可以扩展其他Xtend类,甚至Java类也可以从Xtend类继承。如果没有指定超类型,则使用对象

最简单的类看起来像这样:

class MyClass {

}

Xtend中的一个更高级的泛型类声明:

class ArrayList<E> extends AbstractList<E>

        implements List<E>, RandomAccess, 

                   Cloneable, java.io.Serializable {

  ...

}

构造函数

Xtend类可以定义任意数量的构造函数。与Java不同,您不必一遍又一遍地重复类的名称,而是使用关键字new声明构造函数。构造函数第一行使用this(args...)可以委托给其他构造函数。

class MyClass extends AnotherClass {

  new(String s) {

    super(s)

  }

  

  new() {

    this("default")

  }

}

关于继承,使用于Java相同的规则,即如果超类没有定义无参数构造函数,则构造函数体中第一个表达式必须使用super(args...)的来明确地调用。

构造函数的默认可见性public,但你也可以指定一个明确的可publicprotectedpackageprivate

字段

一个字段可以有一个初始化器。final字段使用val声明,同时引入var声明final字段并能够省略。用val或者var声明字段如果存在初始化表达式,则可以推断字段的类型。关键字finalval同义词。标记为static将被编译为静态Java字段。

class MyClass {

  int count = 1

  static boolean debug = false

  var name = 'Foo'          // type String is inferred 

  val UNIVERSAL_ANSWER = 42 // final field with inferred type int

  ...

}

字段的默认可见性是private。您也可以显式声明其为publicprotectedpackageprivate

Xtend的专长是提供扩展方法的字段,参见扩展方法。

方法

Xtend方法在类中声明,并被转换为具有完全相同签名的相应的Java方法。唯一的例外是调度dispatch方法稍后解释

def String first(List<String> elements) {

  elements.get(0)

}

方法声明以关键字def开始。方法的默认可见性是public。你可以明确地声明其为publicprotectedpackageprivate

Xtend支持static方法的修饰符,如果没有明确给出类型,可以推断返回类型:

def static createInstance() {

  new MyClass('foo')

}

Java一样,vararg参数是允许的,可以作为方法体中的数组值使用:

def printAll(String... strings) {

  strings.forEach[ s | println(s) ]

}

可以从方法体推断方法的返回类型。递归方法和抽象方法必须显式声明返回类型。

抽象方法

Xtend中的抽象方法不定义一个方法体,必须在abstract类或接口中声明。另外指定返回类型是强制性的,因为它不能被推断。

abstract class MyAbstractClass() {

  def String abstractMethod() // 没有方法体

}

方法重写

可以重写override超类方法,或使用关键字override实现接口方法。如果方法覆盖了超类的方法,则必需override关键字替换关键字def。覆盖语义与Java中的相同,例如,不可能覆盖final方法或不可见方法。覆盖方法从超继承其返回类型声明。

override String second(List<String> elements) {

  elements.get(1)

}

声明异常

Xtend不强制您捕获catch或声明检查异常。不过,您仍然可以Java那样,在方法体中使用throws子句声明抛出异常。

如果您没有在方法中声明检查异常,但可能会您的代码抛出异常,编译器会静默地抛出被检查的异常(使用Lombok引入的sneaky-throw技术)。

/*

 * throws an Exception抛出异常

 */

def void throwException() throws Exception {

   throw new Exception

}

/*

 * throws an Exception without declaring it抛出异常而不声明它

 */

def void sneakyThrowException() {

   throw new Exception

}

支持检查异常的可选验证,也可以在相应的Eclipse首选项页面上配置Xtend错误和警告。

推断返回类型

如果方法的返回类型可以从其方法体推断出来,则不必声明它。请看:

def String second(List<String> elements) {

  elements.get(1)

}

可以这样声明:

def second(List<String> elements) {

  elements.get(1)

}

返回类型对于抽象方法声明以及递归实现是强制性的。

通用方法

您可以在方法上指定类型参数。上一节中方法的参数化变体可如下所示:

def <T> second(List<T> elements) {

  elements.get(1)

}

支持类型参数和边界约束,并且共享Java语言规范中定义的语法和语义。

操作符声明

Xtend支持操作重载,是基于操作员-名称-映射,操作部分所述。要声明一个操作符,可以使用操作符的名称声明一个简单的方法,也可以直接使用操作符,如下所示:

class Money {

def + (Money other) { ... }

def - (Money other) { ... }

def * (BigDecimal times) { ... }

...

}

调度(Dispatch)方法

通常,Java一样,方法解析和绑定是在编译时静态地进行的。方法调用根据参数的静态类型进行绑定。有时这不是你想要的。特别是在扩展方法的上下文中,您需要具有多态性。

使用关键字dispatch声明调度方法。

def dispatch printType(Number x) { 

  "it's a number" 

}

def dispatch printType(Integer x) { 

  "it's an int" 

}

对于当前类型层次结构中,具有相同名称和相同数量参数的一组可见调度方法编译器推断出合成调度方法。此调度使用所有参数的常用超类型声明。实际调度方法名称前加上一个下划线,如果这些方法被定义为public方法,那么这些方法的可见性将被protected的范围。客户端代码总是绑定到合成的调度方法。

对于上述示例中的两个调度方法,将生成以下Java代码:

protected String _printType(final Number x) {

  return "it\'s a number";

}

protected String _printType(final Integer x) {

  return "it\'s an int";

}

public String printType(final Number x) {

  if (x instanceof Integer) {

    return _printType((Integer)x);

  } else if (x != null) {

    return _printType(x);

  } else {

    throw new IllegalArgumentException("Unhandled parameter types: " +

      Arrays.<Object>asList(x).toString());

  }

}

请注意,instanceof级联被排序,以便首先处理更具体的类型。

调度例的默认可见性是protected。如果所有调度方法都明确声明相同的可见性,那么这也将是推断调度的可见性。否则是public。参数类型的比较从左到右执行。在下面的例子中,第二种方法声明被认为更具体,因为它的第一个参数类型是最具体的:

def dispatch printTypes(Number x, Integer y) { 

  "it's some number and an int" 

}

def dispatch printTypes(Integer x, Number y) { 

  "it's an int and a number" 

}

生成以下Java代码:

public String printTypes(final Number x, final Number y) {

  if (x instanceof Integer

       && y != null) {

    return _printTypes((Integer)x, y);

  } else if (x != null

       && y instanceof Integer) {

    return _printTypes(x, (Integer)y);

  } else {

    throw new IllegalArgumentException("Unhandled parameter types: " +

      Arrays.<Object>asList(x, y).toString());

  }

}

代码以不能为null的方式进行编译。如果要处理null可以通过使用参数类型Void调度来处理。

def dispatch printType(Number x) { 

  "it's some number" 

}

def dispatch printType(Integer x) { 

  "it's an int" 

}

def dispatch printType(Void x) { 

  "it's null" 

}

这将编译为以下Java代码:

public String printType(final Number x) {

  if (x instanceof Integer) {

    return _printType((Integer)x);

  } else if (x != null) {

    return _printType(x);

  } else if (x == null) {

    return _printType((Void)null);

  } else {

    throw new IllegalArgumentException("Unhandled parameter types: " +

      Arrays.<Object>asList(x).toString());

  }

}

调度方法和继承

所有超类型的所有可见Java方法都符合编译调度方法的表示形式,也调试包括的范围。意味着它们具有预期的参数数,并且具有相同的前缀下划线编译名称。

例如,考虑以下Java类:

public abstract class AbstractLabelProvider {

   protected String _label(Object o) {

      // some generic implementation一些通用实现

   }

}

以下Xtend类扩展Java类:

class MyLabelProvider extends AbstractLabelProvider {

   def dispatch label(Entity it)  {

     name

   }

     

   def dispatch label(Method it) { 

     name+"("+params.join(",")+"):"+type

   }

   

   def dispatch label(Field it) { 

     name+type

   }

}

生成的JavaMyLabelProvider中的dispatch方法如下所示:

public String label(final Object it) {

  if (it instanceof Entity) {

    return _label((Entity)it);

  } else if (it instanceof Field) {

    return _label((Field)it);

  } else if (it instanceof Method) {

    return _label((Method)it);

  } else if (it != null) {

    return super._label(it);

  } else {

    throw new IllegalArgumentException("Unhandled parameter types: " +

      Arrays.<Object>asList(it).toString());

  }

}

静态调度方法

还支持静态调度方法。禁止静态和非静态调度方法的混合。

Create(创建)方法

XtendCreate创建方法允许通常需要两遍图形一次进行,这意味着您不需要两个阶段,单独将一个图形翻译到另一个图形:树结构和树结点互连。您基本上只需要使用Create创建方法编写整个转换,内置的身份保护,负责其余内容。

假如你想创建一个以下的人员列表的副本:

Fred Flintstone {

marriedTo Willma Flintstone

friendWith Barny Rubble

}

Willma Flintstone {

marriedTo Fred Flintstone

}

Barny Rubble {

friendWith Fred Flintstone

}

以下功能可以做到这一点:

def List<Person> copyPersons(List<Person> persons) {

persons.map[copy]

}

def copy(Person p) {

val result = new Person()

result.name = p.name

// 以下是错误的,并导致堆栈溢出

result.friendWith = p.friendWith.map[copy]   

result.marriedWith = p.marriedWith.map[copy]

}

该代码的问题是我们不追踪创建的副本的源。这是模型转换的主要问题。经典的解决方案是在通过两次复制。首先我们创建所有的实例,然后建立链接。虽然它工作,但导致杂乱和非一致代码。Xtendcreate创建功能通过引入身份保护,跟踪每个创建的实例的起源来处理这个问题。因此,create功能需要两个表达式。一个用于实例化实际对象,另一个用于初始化它。

def create result: new Person copy(Person p) {

result.name = p.name

// now it works

result.friendWith = p.friendWith.map[copy]   

result.marriedWith = p.marriedWith.map[copy]

}

如果没有指定结果变量的名称,那么它将被认为是隐式接收变量it,它可以在主体内的特征调用中跳过。此外,您可以定义create函数的返回类型:

def Person create new PersonImpl() copy(Person p) {

/* it.*/name = p.name

friendWith = p.friendWith.map[copy]   

marriedWith = p.marriedWith.map[copy] 

}

怎么工作的

除了关键字create之外,一个指定两个表达式。第一个表达式是创建一个实例工厂,而第二个将进一步初始化它。在调用工厂表达式之前,执行缓存查找以查找先前相同参数创建的实例。如果没有此类实例,则会对工厂表达式进行计算,并将结果存储在缓存中随后,主表达式(也称为初始化表达式)进行了计算。只有缓存中没有先前创建的实例,才会发生这种情况。如果该表达式反过来用创建函数,并传递相同的参数集,则返回先前实例化和缓存的对象。请注意,该对象可能正在初始化。也就是说,其内部状态可能尚不可用。缓存的生命周期附加到Xtend声明类的实例。那就是你可以通过Guice来控制缓存的存储时间。

 

注解

有关类字段方法和参数的注解,它们以@字符为前缀,并接受一些键值对或者注属性的默认值value。数组注值也可以处理单个值。数组封闭在数组字面值中#['first', 'second']。注的语义与Java语言规范中的定义完全相同。这是一个例子:

@TypeAnnotation("some value")

class MyClass {

  @FieldAnnotation(value = @NestedAnnotation(true))

  static val CONSTANT = 'a compile-time constant'

  

  @MethodAnnotation(constant = CONSTANT)

  def String myMethod(@ParameterAnnotation String param) {

    //...

  }

}

此外,积极的注解(Active Annotations允许用户参与将Xtend代码编译为Java源代码。

扩展方法

扩展方法允许向现有类型添加新方法,而无需修改它们。这个特性(功能实际上是Xtend名字的由来(Extension 。它们是基于一个简单的句法技巧:代替向调用的扩展方法括号内第一个参数传递参数,该方法可以使用第一个参数作为其接收器来调用 ——该方法可以像是参数类型的成员一样被调用

"hello".toFirstUpper() // calls StringExtensions.toFirstUpper("hello")

扩展语法中的方法调用通常会导致更可读的代码,因为它们被链接而不是嵌套。扩展的另一个好处是可以特定上下文应用程序层,添加方法。

例如,您可能不想将UI特定的方法和依赖项放入您的域模型类。因此,此功能通常在静态方法中或实用程序类服务层中的方法定义。但是如果你这样调用方法,代码的可读性就会降低,面向对象更少。在Java中,您经常会看到如下代码:

persistenceManager.save(myObject);

把你的实体绑定到persistenceManager,扩展方法可以让你这样

myObject.save

有不同的途径,使方法可用作扩展,这在以下部分中描述。

从库扩展

Xtend库从Java SDK现有类型中提供了非常有用的扩展方法。

"hello".toFirstUpper // calls StringExtensions.toFirstUpper(String)

listOfStrings.map[ toUpperCase ] // calls ListExtensions.<T, R>map(List<T> list,          

      Function<? super T, ? extends R> mapFunction)

可以通过察看库的JavaDoc了解以下可用的功能:

注:源代码在https://github.com/eclipse/xtext-lib/blob/master/org.eclipse.xtext.xbase.lib/src/org/eclipse/xtext/xbase/lib

· ObjectExtensions

· IterableExtensions

· MapExtensions

· ListExtensions

· CollectionExtensions

· BooleanExtensions

· IntegerExtensions

· FunctionExtensions

局部扩展方法

当前类的所有可见的非静态方法及其超类型将自动作为扩展使用。例如

class MyClass {

  def doSomething(Object obj) {

    // do something with obj

  }

  

  def extensionCall(Object obj) {

    obj.doSomething()  // calls this.doSomething(obj)

  }

}

使用局部静态方法必须像其他静态方法一样通过导入获得

扩展导入

Java中,您通常会使用静态方法编写一个辅助类,以便用额外的行为来装饰现有的类。为了整合这样的静态辅助类,Xtend允许,在静态导入把关键字extension放在关键词static面,从而使全部导入的静态函数作为扩展方法。

导入声明

import static extension java.util.Collections.singletonList

允许我们像这样使用singletonList方法:

new MyClass().singletonList() 

// calls Collections.singletonList(new MyClass())

扩展提供者

通过将extension关键字添加到字段局部变量或参数声明中,其实例方法为扩展方法。

想象一下,你想要在一个Person类上有一些层特定功能。也就是一个类似servlet的类,并希望使用一些持久化机制来保持Person。让我们假设Person实现一个通用的接口Entity。你可以有以下接口:

interface EntityPersistence {

  public save(Entity e);

  public update(Entity e);

  public delete(Entity e);

}

如果您已经获得了这种类型一个实例(通过工厂或依赖注入或者其他):

class MyServlet {

  extension EntityPersistence ep = Factory.get(EntityPersistence)

  ...

  

}

您可以这样保存更新和删除任何实体:

val Person person = ...

person.save  // calls ep.save(person)

person.name = 'Horst'

person.update  // calls ep.update(person)

person.delete  // calls ep.delete(person)

extension对值的修饰符,比静态扩展导入有着显着优点:您的代码不受实际实现的扩展方法的约束。您可以通过提供不同的实例简单地将提供引用扩展的组件与外部的另一个实现进行交换。

接口声明

接口声明与Java类似。一个接口可以声明字段,默认final static必须具有一个初始值。当然,方法可以被声明,默认public。接口可以扩展任意数量的其他接口,并可以声明类型参数。这里有一个例子:

interface MyInterface<T> extends OtherInterface {

  val CONSTANT = 42

  def T doStuff(String ... varArg) throws SomeException

}

Java语言版本8以来,接口允许包含非抽象实例方法,称为默认方法,以及静态方法。Xtend(从2.8开始)也支持这一点:如果选择Java 8作为目标语言版本,则允许接口使用如下例所示的方式声明方法。

interface MyInterface {

  def doStuff() {

      'This is an instance method returning a string.'

  }

  static def doGlobalStuff() {

      'This is a static method returning a string.'

  }

}

非抽象实例方法的行为与Java默认方法的行为相当。由于接口可以扩展多个其他接口,因此继承了方法的不同实现时可能会发生多重继承冲突:

interface A {

    def execute() {

        return 1

    }

}

interface B {

    def execute() {

        return 2

    }

}

interface C extends A, B {

}

class D implements A, B {

}

由于方法的多重继承,接口C和类Dexecute()方法,都被标记为错误。解决问题有四种方法。

· 方法重新声明为抽象(非抽象类不允许):

override execute()

· 覆盖实施:

override execute() {

    return 3

}

· 请参考一个超类型的实现:

override execute() {

    A.super.execute()

}

· 首先避免继承方法的多个实现。这是推荐的方法。

注解类型声明

类型也可以被声明。由关键字annotation引入并使用简明的语法声明其值:

annotation MyAnnotation {

  String[] value

  boolean isTricky = false

  int[] lotteryNumbers = #[ 42, 137 ]

}

枚举类型声明

枚举类型如下所示:

enum MyColor {

  GREEN,

  BLUE,

  RED

}

嵌套类型声明

枚举和接口声明可以嵌套。就像Java嵌套枚举一样,注和接口总是静态的。在Xtend中,嵌套类也总是静态的。嵌套类型默认为public,只能嵌套在类接口或注声明中。

class MyClass {

  static class NestedClass {}

  annotation NestedAnnotation {}

  enum NestedEnum {}

  interface NestedInterface {}

}

interface MyInterface {

  static class NestedClass {}

  annotation NestedAnnotation {}

  enum NestedEnum {}

  interface NestedInterface {}

}

annotation MyAnnotation {

  static class NestedClass {}

  annotation NestedAnnotation {}

  enum NestedEnum {}

  interface NestedInterface {}

}

  • 2
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值