Groovy và sự khác biệt với Java
Bài đăng này đã không được cập nhật trong 6 năm
Groovy - một ngôn ngữ lập trình động chạy trên nền máy ảo Java, có các tính năng tương tự như Python hoặc Ruby . Nó hỗ trợ đến 99% cú pháp của Java, do đó việc bạn paste 1 đoạn code Java để chạy trên Groovy là điều hết sức đơn giản và thân thiện với Java Developer.
Làm việc với Groovy từ năm 2013 khi Google IO công bố Android Studio sử dụng Gradle build với groovy script.
Nhưng cho đến bây giờ, mình vẫn chưa có cơ hội sử dụng Groovy như một ngôn ngữ thay thế trong dự án thực tế.
Mọi thứ với mình vẫn chỉ dừng lại ở việc config build script trong Gradle , hoặc Jenkins pipeline.
Vì vậy, với tất cả những gì mình tìm hiểu và một chút kinh nghiệm của bản thân, hy vọng các bạn cùng đóp góp ý kiến nếu có điều gì không đúng. Xin cảm ơn!!
1. Default imports
Việc import
package mỗi khi sử dụng class chẳng hạn như java.io.File
vẫn thường là bắt buộc cho mỗi Java Developer.
Nhưng trong groovy thì không cần làm điều đó, mặc định các packages sau đã imported.
java.io.*
java.lang.*
java.math.BigDecimal
java.math.BigInteger
java.net.*
java.util.*
groovy.lang.*
groovy.util.*
2. Multi-methods
Giả sử chúng ta có đoạn code với 2 method nạp chồng như bên dưới. Compile và chạy cho cả Java và Groovy --> sẽ đem đến 2 kết quả khác nhau.
int method(String arg) {
return 1;
}
int method(Object arg) {
return 2;
}
Object o = "Object";
int result = method(o);
Kết quả :
- Java
result
sẽ là2
. - Groovy
result
sẽ là1
.
Với Java thì các method sẽ được chọn khi compile
, dựa trên data type đã khai báo trong ví trên là Object
Còn với Groovy thì các method sẽ được chọn khi runtime
, nó cũng dựa trên data type đã khai báo. Nhưng khi runtime, o
lúc này là String
object. Vì vậy 1
là kết quả trên Groovy
==> Điều này được gọi là runtime dispatch or multi-methods
trong Groovy.
3. Khởi tạo một Array
Với Java, việc khởi tạo array có thể thực hiện như sau :
int[] array = { 1, 2, 3}
Nhưng trong groovy, nó không được phép. Vì cặp { }
chỉ dành riêng cho closures
.
Thay vì đó sẽ sử dụng như sau để thay thế :
int[] array = [1,2,3]
4. Package scope visibility
Trong Java :
class Person {
String name
}
Trong groovy, không có khái niệm access modifier dành cho field.
Điều đó có nghĩa là , dù bạn có thêm / loại bỏ modifier của field giống như Java thì cũng không ảnh hưởng gì đến khả năng truy xuất của field đó cả.
Để làm điều đó,cần sử dụng annotation @PackageScope
cái sẽ kết hợp với getter
và setter
của field đó ( getter và setter được ngầm định tạo ra trong groovy)
class Person {
@PackageScope String name
}
5. ARM blocks
ARM (Automatic Resource Management) được hỗ trợ từ Java 7. Trong khi Groovy lại không hỗ trợ. Thay vào đó groovy cung cấp các cách khác nhau dựa trên closures
- cái có hiệu quả tương đương trong khi closures
đem lại sự ngắn gọn hơn
// Java
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
}
//Hoặc closer to Java
new File('/path/to/file').withReader('UTF-8') { reader ->
reader.eachLine {
println it
}
}
6. Inner classes
Việc tạo một anonymous inner classes và nested classes vẫn tuân thủ theo quy tắc của Java. Tuy nhiên trong groovy cú pháp có phần đơn giản hơn và loại bổ một số syntax , không bổ từ truy xuất, biến local không cần là final
Static inner classes
class A {
static class B {}
}
new A.B()
Anonymous Inner Classes
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)
Tạo instances of Non-Static Inner Classes
//Java
public class Y {
public class X {}
public X foo() {
return new X();
}
public static X createX(Y y) {
return y.new X();
}
}
Cú pháp y.new X();
không được hỗ trợ trong groovy. Thay vào đó , chúng ta phải viết new X(y)
như bên dưới :
//Groovy
public class Y {
public class X {}
public X foo() {
return new X()
}
public static X createX(Y y) {
return new X(y)
}
}
7. Lambdas
Java 8 có Lambdas
thì groovy có Closures
.
Đây là cách Java 8 thể hiện với lambdas :
Runnable run = () -> System.out.println("Run");
list.forEach(System.out::println);
Còn với Groovy
Runnable run = { println 'run' }
list.each { println it } // or list.each(this.&println)
8. GStrings
Trong Java để nối các string với nhau chúng ta thường dùng dùng toán tử +
để nối chuỗi, giống như thế này:
String name = "Nguyen Van Manh";
System.out.println("Xin chao " + name + "!");
Còn trong groovy thì đơn giản và dễ dàng hơn rất nhiều.
String name = "Nguyen Van Manh";
println "Xin chao ${name}!"
Và cặp Double-quoted string literals ${}
như trên được gọi là GString
value.
9. String và Character literals
Trong Groovy, Singly-quoted được sử dụng cho String
và double-quoted được sử dụng cho String
hoặc GString
(nếu có interpolation GString ${}
)
assert 'c'.getClass()==String
assert "c".getClass()==String
assert "c${1}".getClass() in GString
Kiểu dữ liệu char
chỉ được hiểu chỉ khi khi báo tường minh một biến là 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) {
}
Casting Character
Groovy hỗ trợ 2 kiểu casting từ String
sang --> char
. Ở đây cũng có sự khác biệt trong cách cast.
//Cách 1
assert ((char) "c").class==Character // OK
assert ((char) "cccc").class==Character // Error GroovyCastException
//Cách 2
assert ("c" as char).class==Character // OK
assert ("abc" as char).class==Character // OK, nhưng chỉ lấy ký tự a đầu tiên
10. Primitives và wrappers
Groovy sử dụng Object
cho tất cả mọi thứ, kiểu dữ liệu primitive sẽ tự động wrap tới class wrapper tương úng.
Primitive wrappers table
Primitive type | Wrapper class |
---|---|
boolean | Boolean |
char | Character |
short | Short |
int | Integer |
long | Long |
float | Float |
double | Double |
Cùng xem ví dụ bên dưới để thấy sự khác biệt khi runtime sử dụng Java và Groovy.
int i
m(i)
void m(long l) {
println "in m(long)" // method Java sẽ gọi
}
// method Grovy sẽ gọi, auto wrap int --> Integer
void m(Integer i) {
println "in m(Integer)"
}
11. So sánh ==
Trong Java, so sánh ==
được hiểu là đang so sánh 2 giá trị primitive hoặc nhận dạng các objects.
Trong Groovy ==
được hiểu là a.compareTo(b)==0
nếu ab là Comparable
, ngược lại sẽ được hiểu là a.equals(b)
12. Conversions
Bảng so sánh việc chuyển đổi kiểu dữ liệu giữa Java và Groovy.
Y
: Yes, Java có thể chuyển đổiC
: Yes, nhưng cần cast một cách tường minhT
: Yes, nhưng dữ liệu sẽ bị bỏ bớtN
: No, Không cho phép
Java Conversions Table
Converts from | boolean | byte | short | char | int | long | float | double |
---|---|---|---|---|---|---|---|---|
boolean | - | N | N | N | N | N | N | N |
byte | N | - | Y | C | Y | Y | Y | Y |
short | N | C | - | C | Y | Y | Y | Y |
char | N | C | C | - | Y | Y | Y | Y |
int | N | C | C | C | - | Y | T | Y |
long | N | C | C | C | C | - | T | T |
float | N | C | C | C | C | C | - | Y |
double | N | C | C | C | C | C | C | - |
Grroovy Conversions Table
Converts from | boolean | Boolean | byte | Byte | short | Short | char | Character | int | Integer | long | Long | BigInteger | float | Float | double | Double | BigDecimal |
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
boolean | - | B | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N |
Boolean | B | - | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N | N |
byte | T | T | - | B | Y | Y | Y | D | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
Byte | T | T | B | - | Y | Y | Y | D | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
short | T | T | D | D | - | B | Y | D | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
Short | T | T | D | T | B | - | Y | D | Y | Y | Y | Y | Y | Y | Y | Y | Y | Y |
char | T | T | Y | D | Y | D | - | D | Y | D | Y | D | D | Y | D | Y | D | D |
Character | T | T | D | D | D | D | D | - | D | D | D | D | D | D | D | D | D | D |
int | T | T | D | D | D | D | Y | D | - | B | Y | Y | Y | Y | Y | Y | Y | Y |
Integer | T | T | D | D | D | D | Y | D | B | - | Y | Y | Y | Y | Y | Y | Y | Y |
long | T | T | D | D | D | D | Y | D | D | D | - | B | Y | T | T | T | T | Y |
Long | T | T | D | D | D | T | Y | D | D | T | B | - | Y | T | T | T | T | Y |
BigInteger | T | T | D | D | D | D | D | D | D | D | D | D | - | D | D | D | D | T |
float | T | T | D | D | D | D | T | D | D | D | D | D | D | - | B | Y | Y | Y |
Float | T | T | D | T | D | T | T | D | D | T | D | T | D | B | - | Y | Y | Y |
double | T | T | D | D | D | D | T | D | D | D | D | D | D | D | D | - | B | Y |
Double | T | T | D | T | D | T | T | D | D | T | D | T | D | D | T | B | - | Y |
BigDecimal | T | T | D | D | D | D | D | D | D | D | D | D | D | T | D | T | D | - |
Y
: Yes, Groovy có thể chuyển đổiD
: Yes, tự động chuyển đổi khi compile hoặc cần cast một cách tường minhT
: Yes, nhưng dữ liệu sẽ bị bỏ bớtB
: Yes, auto boxing/unboxingN
: No, không cho phép
13. Extra keywords
Ngoài các keywords của Java, thì Groovy còn sử dụng thêm một số keywords bên dưới :
as
def
in
trait
Cũng giống như Java, các keywords cũng không được sử dụng như tên biến.
14. Không sử dụng dấu ;
Với Java, sau mỗi statement sẽ kết thúc bằng 1 dấu chấm phẩy ;
. Nhưng với Groovy thì điều đó là không bắt buộc, tất nhiên có cũng được mà không có cũng không sao. Groovy đều hiểu và compile hoàn toàn bình thường.
Chỉ có điều, nếu dấu ;
được sử dụng, một số IDE sẽ đánh dấu đó là không cần thiết.
15. Từ khóa return - không bắt buộc
static void main(String[] args) {
int i
def result = m(i)
println "Ket qua = ${result}"
}
static int m(Integer i) {
100
}
Với Groovy return
ở một method không còn là bắt buộc. Điều này rất hữu ich với các block code như closures
hoặc các method đơn giản.
Tất nhiên , nó không có nghĩa là chúng ta sẽ bỏ return, vì trong một số trường hợp việc sử dụng return
vẫn là cần thiết.
16. class suffix có thể bỏ qua
.class
suffix có thể bỏ qua khi pass như parameter. Như Person.class
ví dụ bên dưới
static void main(String[] args) {
// có thể hiểu là Person.class
def p = newInstance(Person)
println p.name
}
static Person newInstance(Class<Person> clazz) {
return clazz.newInstance()
}
17. Try Catch Exception
Với Java bạn cần chỉ định tường minh Exception trong catch.
Nhưng với Groovy , đơn giản chỉ cần như sau
try {
Integer.parseInt("aaa")
} catch (e) {
println e.getMessage()
}
Và có thể còn rất nhiều sự khác biệt hay ho nữa trong Groovy.
Tổng kết
Trên đây chỉ là những gì mình tìm hiểu và viết lại theo ý hiểu của bản thân và một số cái được áp dụng khi làm việc thực tế.
Với Groovy nó hỗ trợ đến 99% cú pháp của Java, hơn nữa nó mang lại sự ngắn gọn đơn giản trong cú pháp trình bày. Sẽ giúp chúng ta phát triển ứng dụng một phần nào đó nhanh chóng hơn, một khi Grails ngày càng phổ biến
Để tìm hiểu Groovy, Learning , Documentation vv.. bạn có thể tìm hiểu tại http://groovy-lang.org
All rights reserved