Groovy学习之-与Java的不同之处

Groovy学习目录-传送门

Groovy试图对Java开发人员尽可能自然。 我们试图在设计Groovy时遵循最小惊讶原则,特别是对于来自Java背景的Groovy开发人员。

这里我们列出了Java和Groovy之间的所有主要区别。

默认 imports

所有这些包和类都是默认导入的,您不必使用显式import语句来使用它们:

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

Multi-methods

在Groovy中,将在运行时选择将被调用的方法。 这称为运行时分派或Multi-methods。 这意味着将基于运行时参数的类型来选择方法。 在Java中,则是根据声明的类型,在编译时选择方法。

下面的代码,以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(1, result);

这是因为Java将使用静态信息类型,即o被声明为Object,而Groovy将在运行时选择该方法被实际调用时。 因为它是用String调用的,所以调用String版本。

数组初始化

在Groovy中,{...}块是为闭包而保留的。 这意味着您不能使用以下语法创建数组literal

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

实际上你必须使用:

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

包范围可见性

在Groovy中,在字段上省略修饰符不会像Java中一样变成package-private字段:

class Person {
     字符串名称
}}

相反,它用于创建一个属性,也就是说一个私有字段,一个关联的getter和一个关联的setter

可以通过使用@PackageScope注释来创建一个package-private字段:

class Person {
     @PackageScope字符串名称
}}

自动资源管理块

Groovy不支持Java 7中的ARM(自动资源管理)块。 相反,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();
}

可以这样写:

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

或者,如果你想要一个更接近Java的版本:

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

内部类

匿名内部类和嵌套类的实现遵循Java方式,但是你不应该拿出Java语言规范,并且对不同的东西不断地摇头。 它的实现看起来很像groovy.lang.Closure,同时有一些好处和一些差异。 例如访问私有字段和方法可能成为一个问题,但另一方面,局部变量不必是final的。

  1. 静态内部类
    这里有一个静态内部类的例子:
class A {
    static class B {}
}

new A.B()

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

  1. 匿名内部类
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)
  1. 创建非静态内部类的实例
    在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)。 由于这也可能是正常的方式,我们还没有找到一个好的方法来防止这个问题。

Lambdas

Java 8支持lambdas和方法引用:

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

Java 8 lambdas可以或多或少被认为是匿名内部类。 Groovy不支持该语法,但是可以使用闭包:

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

GStrings

由于双引号字符串字面量被解释为GString,如果具有包含美元字符的String的类是使用Groovy和Java编译器编译的,Groovy可能会失败并产生编译错误或产生细微不同的代码。

通常,Groovy将在GStringString之间自动转换,如果API声明参数的类型,小心接受Object参数的Java API,然后检查实际的类型。

字符串和字符

Groovy中的单引号用于String,双引号结果是StringGString,取决于文字中是否有插值。

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

只有在赋给char类型的变量时,Groovy会自动将单字符String转换为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的情况下,在转换multi-char 时存在微妙的差别。 Groovy风格的转换是更宽松的,将采取第一个字符,而C风格的转换将失败,异常。

// 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'

原始和封装

因为Groovy使用Objects来做每一件事,它对原始的引用自动包装。 因此,它不遵循Java的行为扩展优先于装箱。 这里有一个使用int的例子

int i
m(i)

//这是Java将调用的方法,因为扩展优先于拆箱。
void m(long l) {
  println "in m(long)"
}

//这是Groovy实际调用的方法,因为所有的基本引用都使用它们的包装类。
void m(Integer i) {
  println "in m(Integer)"
}

==的行为

在Java中==表示对象的原始类型或标识的相等性。 在Groovy ==翻译为a.compareTo(b)== 0,如果他们是可比较的,否则a.equals(b)。 如果要检查身份,有is方法,例如a.is(b)

转换

详情请点击超链接

额外的关键字

Groovy中还要比Java多几个关键字。 不要将它们用于变量名称等。

  • as
  • def
  • in
  • trait

你可能感兴趣的:(Groovy学习之-与Java的不同之处)