Groovy 和 Java 的不同之处
闲话    Groovy Java    2016-08-28 05:37:39    646    0    0

这些都是官方提供的 http://www.groovy-lang.org/differences.html

Groovy 尽量的让 Java 开发者能够自然的使用,我们追随的原则是:对于有 Java 背景的开发者开始 Groovy 的学习时,不必感到太过惊讶。

1. 默认导入

以下包是默认导入的,也就是说,不必为了使用它们而使用显示的 import 语句。

  • java.io.*
  • java.lang.*
  • java.math.BigDecimal
  • java.math.BigInteger
  • java.net.*
  • java.util.*
  • groovy.lang.*
  • groovy.util.*

2. 方法重载

在 Groovy 中,方法在运行时被选择和反射执行,这被称作运行时分派或者方法重载。这意味着方法的选择基于参数在运行时的类型。在 Java 中使用:方法的选择基于参数在编译时的类型,也就是声明的类型。

下面的程序在 Java 和 Groovy 中都能编译通过,但是却有着不同的行为:

int method(String arg) {
    return 1;
}
int method(Object arg) {
    return 2;
}
Object o = "Object";
int result = method(o);

在 Java 中你可以得到:

assertEquals(2, result);

而在 Groovy 中:

assertEquals(2, result);

那是因为 Java 使用静态类型信息,变量 o 被声明为 Object ,然而 Groovy 是在运行时,方法要被调用的时候才做决定的。那个时候它的类型是 String ,所以参数是 String的方法被调用。

3. 数组初始化

在 Groovy 中,{...} 这种结构被用来声明闭包,那就意味着你不能用以下语法声明数组了:

int[] array = { 1, 2, 3};

实际上,你可以使用以下代码作为替代:

int[] array = [1, 2, 3]

4. 包空间可见性

在 Groovy 中,省略声明的属性和 Java 中省略声明表示包级私有行为不同。

class Person {
    String name
}

这种写法,Groovy 中会创建一个属性,也就是说,这是一个私有字段,可以通过自动生成的 getter 和 setter 方法访问。

要创建一个包级私有的属性也是可以的,使用注解 @PackageScope

class Person {
    @PackageScope String name
}

5. ARM 块

Java 7 中添加的 ARM( Automatic Resource Management 自动资源管理 )块在Groovy 中不支持。Groovy 中提供的重多基于闭包的方法可以做为替代,功效相同,更符合习惯。例如:

Path file = Paths.get("/path/to/file");
Charset charset = Charset.forName("UTF-8");
try (BufferedReader reader = Files.newBufferedReader(file, charset)) {
    String line;
    while ((line = reader.readLine()) != null) {
        System.out.println(line);
    }


} catch (IOException e) {
    e.printStackTrace();
}

Groovy 可以写成:

new File('/path/to/file').eachLine('UTF-8') {
   println it
}

如果你想更接近 Java, 还可以写成:

new File('/path/to/file').withReader('UTF-8') { reader ->
   reader.eachLine {
       println it
   }
}

6. 内部类

匿名内部类和嵌套类的实现是 Java 定义的,但是你不能拒绝有不同于 Java 的实现。就像 groovy.lang.Closure 就是这样的一种实现,有些不同也有些便利。访问私有字段和方法是个问题,但是局部变量也不再需要添加 final 了。

6.1 静态内部类

这是一个静态内部类的例子:

class A {
    static class B {}
}


new A.B()

静态内部类的使用是支持最好的,如果你确实需要一个内部类,那就做一个静态的。

6.2 匿名内部类

import java.util.concurrent.CountDownLatch
import java.util.concurrent.TimeUnit


CountDownLatch called = new CountDownLatch(1)


Timer timer = new Timer()
timer.schedule(new TimerTask() {
    void run() {
        called.countDown()
    }
}, 0)


assert called.await(10, TimeUnit.SECONDS)

6.3 实例化一个非静态内部类

在 Java 中你可以这么做:

public class Y {
    public class X {}
    public X foo() {
        return new X();
    }
    public static X createX(Y y) {
        return y.new X();
    }
}

Groovy 中不支持 y.new(x) 语法,作为替代,你要写成 new X(y),就像下面的代码。

public class Y {
    public class X {}
    public X foo() {
        return new X()
    }
    public static X createX(Y y) {
        return new X(y)
    }
}

注意,Groovy 支持调用只有一个参数的方法的时候不给参数。参数实际上会是 null,构造函数也是基于相同的规则。用 new X() 代替 new X(this) 是非常危险的,由于这种可能也是符合规则的,我们目前没有找到好的解决方案。

7.Lambdas 表达式

Java8 中支持 lambdas 表达式和方法引用。

Runnable run = () -> System.out.println("Run");
list.forEach(System.out::println);

Java8 的 lambdas 表达式多少可以理解为匿名内部类,Groovy 不支持那样的语法,但是可以用闭包替代。

Runnable run = { println 'run' }
list.each { println it } // or list.each(this.&println)

8. GStrings

双引号好中的字符串字面量会被解释为GString 值,Groovy 在遇到包含美元符的字符串时可能会编译失败,或者产生与 Java 编译器略微不同的代码。

通常情况下,如果一个 API 声明了这种类型的参数,Groovy 在 GStirngString 之间会自动转换,注意,如果 Java API 使用 Object 类型的时候会检测真实类型。

9. 字符串和字符字面量

单引号字面量在 Groovy 中也使用 String 类型,双引号有时使用 String 有时使用 GString ,取决于字面量中有没有插入值。

assert 'c'.getClass()==String
assert "c".getClass()==String
assert "c${1}".getClass() in GString

只有在指定变量类型为 char 的时候,Groovy 自动将单字符从 Stirng 转为 char 。当调用参数是 char 的方法时,我们需要确定类型已经被转换了,或者明确的转换它。

char a='a'
assert Character.digit(a, 16)==10 : 'But Groovy does boxing'
assert Character.digit((char) 'a', 16)==10


try {
  assert Character.digit('a', 16)==10
  assert false: 'Need explicit cast'
} catch(MissingMethodException e) {
}

Groovy 支持两种类型的类型转换风格,在转为 char 这个问题上,它们的表现略有不同。

// for single char strings, both are the same
assert ((char) "c").class==Character
assert ("c" as char).class==Character


// for multi char strings they are not
try {
  ((char) 'cx') == 'c'
  assert false: 'will fail - not castable'
} catch(GroovyCastException e) {
}
assert ('cx' as char) == 'c'
assert 'cx'.asType(char) == 'c'

10. 基础类型和包装类型

因为 Groovy 中一切皆对象,基础类型会被自动装箱。因此,它不再符合 Java 的重载匹配优先级,下面用 int 举例说明。

int i
m(i)


void m(long l) {       //[1]    
  println "in m(long)" 
}


void m(Integer i) {        
  println "in m(Integer)" //[2]
}
  • Java 中会去调用方法 【1】,重载会先匹配未装箱的参数。
  • Groovy 中会调用方法 【2】,所有基础类型都使用它的包装类。

11. == 的行为

在 Java 中,== 意味着原始类型或者对象的 Id相同。在 Groovy 中,== 对于实现了 Comparable 的对象翻译为 a.compareTo(b) == 0 ,对于其他,翻译为 a.equals(b)。检查一个对象的 id 使用 is 方法。例如, a.is(b)

12. 类型转换

Java 会进行一些自动的类型扩大或截断的转换

java

goovy 做了极大的扩展

groovy

  • Y 代表可以直接转换

  • D 代表编译期推断或者声明

  • B 代表自动装箱拆箱

  • C 代表需要声明

  • T 代表转换会带来数据截断

  • N 代表不能转换

转换为 boolean/Boolean 时会被截断;将一个数字转为字母时使用 Number.intvalue() 强转为 charFloat or Double 转为 BigInteger and BigDecimal Groovy 使用 Number.doubleValue(),其他情况使用 toString()。其他转换行为由 java.lang.Number 定义。

13. 更多的关键字

Groovy 中比 Java 中的关键字更多,不要用它们作为变量名:

  • as
  • def
  • in
  • trait
文档导航