Groovy là một ngôn ngữ lập trình động, mạnh mẽ, chạy trên máy ảo Java (JVM), mang đến nhiều tính năng tương tự như Python hay Ruby. Với khả năng tương thích lên đến 99% cú pháp Java, Groovy cho phép bạn dễ dàng sử dụng lại các đoạn code Java hiện có. Tuy nhiên, Groovy cũng có những khác biệt quan trọng so với Java mà các nhà phát triển cần nắm vững. Bài viết này sẽ đi sâu vào những điểm khác biệt đó, giúp bạn hiểu rõ hơn về Groovy và cách nó có thể giúp bạn viết code hiệu quả hơn.
Từ năm 2013, khi Google I/O công bố Android Studio sử dụng Gradle build với Groovy script, tôi đã bắt đầu làm việc với Groovy. Tuy nhiên, đến nay, tôi vẫn chưa có cơ hội sử dụng Groovy như một ngôn ngữ thay thế hoàn toàn trong các dự án thực tế. Kinh nghiệm của tôi chủ yếu dừng lại ở việc cấu hình build script trong Gradle hoặc Jenkins pipeline. Dựa trên những gì tôi đã tìm hiểu và một chút kinh nghiệm cá nhân, tôi hy vọng bài viết này sẽ cung cấp cho bạn cái nhìn tổng quan hữu ích về sự khác biệt giữa Groovy và Java.
Mục Lục
- 1 Những Điểm Khác Biệt Cốt Lõi Giữa Groovy và Java
- 1.1 1. Import Package Tự Động
- 1.2 2. Runtime Dispatch (Multi-methods)
- 1.3 3. Khởi Tạo Array
- 1.4 4. Access Modifier
- 1.5 5. Automatic Resource Management (ARM)
- 1.6 6. Inner Classes
- 1.7 7. Lambdas (Java 8) vs. Closures (Groovy)
- 1.8 8. String Interpolation
- 1.9 9. String và GString
- 1.10 10. Kiểu Dữ Liệu char
- 1.11 11. Primitive Types và Wrappers
- 1.12 12. So Sánh Bằng (==)
- 1.13 13. Chuyển Đổi Kiểu Dữ Liệu
- 1.14 14. Keywords
- 1.15 15. Dấu Chấm Phẩy (;)
- 1.16 16. return
- 1.17 17. Suffix .class
- 1.18 18. Exception Handling
- 2 Kết luận
Những Điểm Khác Biệt Cốt Lõi Giữa Groovy và Java
1. Import Package Tự Động
Trong Java, việc import package mỗi khi sử dụng một class, ví dụ như sentayho.com.vn, là điều bắt buộc. Tuy nhiên, Groovy đơn giản hóa điều này bằng cách tự động import một số package mặc định, bao gồm:
java.io.*java.lang.*java.net.*java.util.*groovy.lang.*groovy.util.*java.math.BigDecimaljava.math.BigInteger
2. Runtime Dispatch (Multi-methods)
Xét đoạn code sau với hai method nạp chồng:
int method(String arg) { return 1; }
int method(Object arg) { return 2; }
Object o = "Object";
int result = method(o);
Khi biên dịch và chạy đoạn code này trên Java và Groovy, bạn sẽ nhận được kết quả khác nhau:
- Java:
resultsẽ là 2. - Groovy:
resultsẽ là 1.
Trong Java, method được chọn tại thời điểm biên dịch, dựa trên kiểu dữ liệu đã khai báo (trong ví dụ trên là Object). Ngược lại, Groovy chọn method tại thời điểm runtime, dựa trên kiểu dữ liệu thực tế của đối tượng. Trong trường hợp này, o là một String object tại runtime, do đó method method(String arg) được gọi. Đây được gọi là runtime dispatch hoặc multi-methods trong Groovy.
3. Khởi Tạo Array
Trong Java, bạn có thể khởi tạo một array như sau:
int[] array = { 1, 2, 3};
Tuy nhiên, Groovy không cho phép sử dụng cú pháp này. Thay vào đó, bạn cần sử dụng dấu ngoặc vuông:
int[] array = [1, 2, 3]
Dấu {} trong Groovy được dành riêng cho closures.
4. Access Modifier
Trong Java, bạn sử dụng access modifier (public, private, protected) để kiểm soát khả năng truy cập vào các field của một class. Tuy nhiên, Groovy không có khái niệm access modifier cho field theo cách tương tự.
Trong Groovy, việc thêm hoặc loại bỏ modifier của field không ảnh hưởng đến khả năng truy xuất của field đó. Để kiểm soát khả năng truy cập, bạn cần sử dụng annotation @PackageScope, kết hợp với getter và setter của field (getter và setter được tạo ngầm định trong Groovy).
class Person {
@PackageScope String name
}
5. Automatic Resource Management (ARM)
Java 7 giới thiệu tính năng ARM, giúp tự động giải phóng tài nguyên sau khi sử dụng. Groovy không hỗ trợ ARM trực tiếp, nhưng cung cấp các cách khác dựa trên closures để đạt được hiệu quả tương đương, thường ngắn gọn hơn:
// Java
Path file = sentayho.com.vn("/path/to/file");
Charset charset = sentayho.com.vname("UTF-8");
try (BufferedReader reader = sentayho.com.vnufferedReader(file, charset)) {
String line;
while ((line = sentayho.com.vnLine()) != null) {
sentayho.com.vntln(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 ->
sentayho.com.vnLine { println it }
}
6. Inner Classes
Việc tạo anonymous inner classes và nested classes trong Groovy tuân thủ theo quy tắc của Java, nhưng cú pháp đơn giản hơn và có thể loại bỏ một số thành phần, ví dụ như không cần khai báo biến local là final.
Static inner classes
class A {
static class B {}
}
new A.B()
Anonymous Inner Classes
import sentayho.com.vntDownLatch
import sentayho.com.vnUnit
CountDownLatch called = new CountDownLatch(1)
Timer timer = new Timer()
sentayho.com.vndule(new TimerTask() {
void run() {
sentayho.com.vntDown()
}
}, 0)
assert sentayho.com.vnt(10, sentayho.com.vnNDS)
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 đó, bạn phải viết new X(y):
//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) vs. Closures (Groovy)
Java 8 giới thiệu Lambdas, trong khi Groovy có Closures. Dưới đây là cách sử dụng Lambdas trong Java 8:
Runnable run = () -> sentayho.com.vntln("Run");
sentayho.com.vnach(System.out::println);
Và đây là cách sử dụng Closures trong Groovy:
Runnable run = { println 'run' }
sentayho.com.vn { println it } // or sentayho.com.vn(this.&println)
Nguồn: Baeldung
8. String Interpolation
Trong Java, để nối các string, bạn thường sử dụng toán tử +:
String name = "Nguyen Van Manh";
sentayho.com.vntln("Xin chao " + name + "!");
Groovy cung cấp cách đơn giản hơn với string interpolation:
String name = "Nguyen Van Manh";
println "Xin chao ${name}!"
Cặp Double-quoted string literals ${} được gọi là GString value.
9. String và GString
Trong Groovy, singly-quoted string literals được sử dụng cho String, còn double-quoted được sử dụng cho String hoặc GString (nếu có interpolation ${}).
assert 'c'.getClass()==String
assert "c".getClass()==String
assert "c${1}".getClass() in GString
10. Kiểu Dữ Liệu char
Kiểu dữ liệu char chỉ được hiểu khi khai báo tường minh một biến là char:
char a='a'
assert sentayho.com.vnt(a, 16)==10 : 'But Groovy does boxing'
assert sentayho.com.vnt((char) 'a', 16)==10
try {
assert sentayho.com.vnt('a', 16)==10
assert false: 'Need explicit cast'
} catch(MissingMethodException e) {
}
Casting Character
Groovy hỗ trợ hai cách casting từ String sang char, và có sự khác biệt giữa chúng:
//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
11. Primitive Types và Wrappers
Groovy sử dụng Object cho mọi thứ. Các kiểu dữ liệu primitive sẽ tự động wrap thành các 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 |
Xem ví dụ sau để 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)"
}
12. So Sánh Bằng (==)
Trong Java, toán tử == so sánh giá trị primitive hoặc nhận dạng object. Trong Groovy, == tương đương với a.compareTo(b)==0 nếu a và b là Comparable, ngược lại sẽ tương đương với a.equals(b).
13. Chuyển Đổi Kiểu Dữ Liệu
Dưới đây là bảng so sánh việc chuyển đổi kiểu dữ liệu giữa Java và Groovy:
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 | – |
Groovy 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 | T | D | D | D | D | – |
Y: Có, Groovy có thể chuyển đổi.D: Có, tự động chuyển đổi khi biên dịch hoặc cần cast tường minh.T: Có, nhưng dữ liệu có thể bị mất.B: Có, tự động boxing/unboxing.N: Không, không cho phép.
14. Keywords
Ngoài các keywords của Java, Groovy còn sử dụng thêm một số keywords sau:
asdefintrait
Giống như Java, các keywords không được sử dụng làm tên biến.
15. Dấu Chấm Phẩy (;)
Trong Java, mỗi statement kết thúc bằng dấu chấm phẩy ;. Trong Groovy, dấu chấm phẩy là không bắt buộc. Groovy vẫn hiểu và biên dịch code bình thường dù có hay không có dấu chấm phẩy. Tuy nhiên, một số IDE có thể đánh dấu dấu chấm phẩy là không cần thiết nếu bạn sử dụng chúng.
static void main(String[] args) {
int i
def result = m(i)
println "Ket qua = ${result}"
}
static int m(Integer i) {
100
}
16. return
Trong Groovy, return không bắt buộc ở cuối một method. Điều này rất hữu ích với các block code như closures hoặc các method đơn giản. Tuy nhiên, việc sử dụng return vẫn cần thiết trong một số trường hợp.
17. Suffix .class
Suffix .class có thể bỏ qua khi truyền như parameter. Ví dụ:
static void main(String[] args) {
// có thể hiểu là sentayho.com.vn
def p = newInstance(Person)
println p.name
}
static Person newInstance(Class clazz) {
return sentayho.com.vnnstance()
}
18. Exception Handling
Với Java, bạn cần chỉ định tường minh Exception trong catch. Trong Groovy, bạn có thể viết đơn giản như sau:
try {
sentayho.com.vneInt("aaa")
} catch (e) {
println e.getMessage()
}
Kết luận
Groovy là một ngôn ngữ lập trình linh hoạt và mạnh mẽ, kế thừa nhiều ưu điểm của Java đồng thời mang đến những cải tiến đáng kể về cú pháp và tính năng. Việc nắm vững những điểm khác biệt giữa Groovy và Java sẽ giúp bạn tận dụng tối đa sức mạnh của Groovy trong các dự án phát triển phần mềm. Groovy hỗ trợ đến 99% cú pháp của Java, đồng thời mang lại sự ngắn gọn và đơn giản trong cú pháp trình bày, giúp bạn phát triển ứng dụng nhanh chóng hơn, đặc biệt khi Grails ngày càng trở nên phổ biến.
Để tìm hiểu thêm về Groovy, bạn có thể tham khảo tài liệu chính thức tại sentayho.com.vn.
