Kotlin – Phiên bản nâng cấp của Java

Java là một ngôn ngữ lập trình vô cùng mạnh mẽ nhưng nó cũng có điểm yếu của mình. Vì thế mà tôi khuyên các bạn nên đọc qua quyển “Effective Java.” của Joshua Bloch’s. Đó là một quyển hướng dẫn toàn thư về Java code cũng như liệt kê về lỗi về coding cũng như cách khắc phục chúng. Bao gồm 78 mục, gọi là “Items”, cho người đọc nhiều kiến thức quí giá về Java.

Các ngôn ngữ lập trình mới có một ưu điểm vượt trội là phân tích những điểm yếu của chúng để tự cải thiện bản thân mình. Jetbrains, một công ty về công nghệ khá nổi tiếng nhờ tạo ra nhiều IDEs được cộng đồng nhiệt liệt ủng hộ, đã đưa ra quyết định tạo ra ngôn ngữ lập trình Kotlin vào năm 2010 nhằm phục vụ cho quá trình phát triển project của riêng mình. Mục tiêu đươc đề ra cho Kotlin là trở nên chính xác cũng như dễ hiểu hơn trong khi loại bỏ đi những nhược điểm của Java. Bởi tất cả IDEs của Jetbrain đều dùng Java, thế nên họ cần một ngôn ngữ mới nhưng vẫn có khả năng hoạt động được với Java. Mặt khác, các Java developer cũng dễ dàng chuyển qua xài Kotlin hơn.Với Kotlin, Jetbrains kì vọng tạo ra một phiên bản Java tốt hơn.

Sau khi đọc lại quyển “Effective Java”, tôi nhận ra có khá nhiều thứ không còn áp dụng được với Kotlin, vì thế mà với bài viết này tôi sẽ phân tích rõ ràng về cách dùng Kotlin.

Builders không còn cần thiết với Kotlin’s default value

Khi bạn có quá nhiều parameters cho một constructor trong Java, code sẽ trở nên rất khó đọc cũng như nhiều lỗi. Theo cuốn “effective Jave”, “Item” thứ 2, chỉ cách ta có thể dùng Builder Pattern một cách hiệu quả nhất. Với ví dụ một object có nội dung nói về fact các giá trị dinh dưỡng, chúng ta sẽ cần 2 required paramenters (servingSize, servings) và 4 optional parameters ( calories, fat, sodium, carbohydrates):

   public class JavaNutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
        // Required parameters
        private final int servingSize;
        private final int servings;

        // Optional parameters - initialized to default values
        private int calories      = 0;
        private int fat           = 0;
        private int carbohydrate  = 0;
        private int sodium        = 0;

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings    = servings;
        }

        public Builder calories(int val)
        { calories = val;      return this; }
        public Builder fat(int val)
        { fat = val;           return this; }
        public Builder carbohydrate(int val)
        { carbohydrate = val;  return this; }
        public Builder sodium(int val)
        { sodium = val;        return this; }

        public JavaNutritionFacts build() {
            return new JavaNutritionFacts(this);
        }
    }

    private JavaNutritionFacts(Builder builder) {
        servingSize  = builder.servingSize;
        servings     = builder.servings;
        calories     = builder.calories;
        fat          = builder.fat;
        sodium       = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}

Ngay lập tức, một object trong Java sẽ cho kết quả như sau:

final JavaNutritionFacts cocaCola = new JavaNutritionFacts.Builder(240,8)
    .calories(100)
    .sodium(35)
    .carbohydrate(27)
    .build();

Ngược lại với Kotlin thì lại không hề cần một builder pattern bởi nó có tính nặng gọi là default parameters, cho phép bạn đặt default values cho từng optional constructor argument:

class KotlinNutritionFacts(
        private val servingSize: Int,
        private val servings: Int,
        private val calories: Int = 0,
        private val fat: Int = 0,
        private val sodium: Int = 0,
        private val carbohydrates: Int = 0)

Tạo ra một object trong Kotlin sẽ giống như thế này:

val cocaCola = KotlinNutritionFacts(240,8,
                calories = 100,
                sodium = 35,
                carbohydrates = 27)

Để cho dễ đọc, thì bạn có thể đặt tên cho required parameters là servingSize và servings:

val cocaCola = KotlinNutritionFacts(
                servingSize = 240,
                servings = 8,
                calories = 100,
                sodium = 35,
                carbohydrates = 27)

Cũng như Java, object tạo tại Kontlin cũng là immutable.

Bạn có thể thấy số dòng code đã giảm từ 47 trong Java xuống còn 7 trong Kotlin, kết quả là tốt độ xử lý nhanh lên mấy lần.

Tip: nếu bạn muốn tạo ra KotlinNutrition object trong Java, thì vẫn được thôi, nhưng nó đòi hỏi bạn phải đưa ra value cho từng optional parameter. May thay, là nếu bạn thêm JvmOverloads annotation thì sẽ cho ra được các constructors. Tất nhiên là để dùng được chúng thì bạn cũng sẽ phải thêm vào keyword constructor :slight_smile:

class KotlinNutritionFacts @JvmOverloads constructor(
        private val servingSize: Int,
        private val servings: Int,
        private val calories: Int = 0,
        private val fat: Int = 0,
        private val sodium: Int = 0,
        private val carbohydrates: Int = 0)

Dễ dàng trong việc tạo ra Singleton

Chúng ta luôn muốn tạo ra một Java object nhưng lại có khả năng hoạt động như singleton. Với code sau đây sẽ giúp bạn làm được việc trên:

public class JavaElvis {

    private static JavaElvis instance;

    private JavaElvis() {}

    public static JavaElvis getInstance() {
        if (instance == null) {
            instance = new JavaElvis();
        }
        return instance;
    }

    public void leaveTheBuilding() {
    }
}

Kotlin lại có sẵn object declarations giúp cho object hoạt động như singleton luôn:

object KotlinElvis {

    fun leaveTheBuilding() {}
}

equals() và hashCode()

Đối với functional programming cũng như giữ code đơn giản thì việc dùng immutable value objects là điều không cần phải bàn cãi. immutable ở đây ám chỉ sự không thay đổi. Nói cách khác, class luôn nên là immutable trừ khi bạn bắt buộc phải làm ngược lại. Thế nhưng công việc tạo Immutable value object trong Java rất là chán luôn bởi cứ thêm equals() và hashCode() funtion vào cho từng object một. Chưa kể khi override equals() còn phải bảo đảm contracts luôn được tính nhất quán, đối xứng, etc…. nghe phức tạp như toán học vậy đó.

Trong Kotlin, các bạn chỉ cần dùng data classes, khi đó compiler sẽ tự động làm thay mọi việc cho bạn. Tất cả yêu cầu là bạn type data` vào trước class.

Tip: Gần đây, AutoValue cho Java trở nên vô cùng nổi tiếng. Library có khả năng tạo ra immutable value classes cho Java 1.6+.

Properties thay vì fields
public class JavaPerson {

    // don't use public fields in public classes!
    public String name;
    public Integer age;
}

Hẳn các bạn khi dùng tới Java cũng luôn được chỉ rằng nên dùng accessor thay cho public field trong public class. Bởi nếu không thì bạn sẽ gặp phải nhiều vấn đề bởi field khi đó sẽ có thể vào trực tiếp và mất tính linh hoạt. Nói cách khác khi bạn thay đổi đại diện nội bộ của class (internal representation) thì nó còn ảnh hưởng lên cả public API của nó. Như vậy, bạn cũng sẽ chẳng thể nào đưa ra giới hạn cho value của một field (vd như độ tuổi từ 18 trở lên chẳng hạn). Đó là nguyên nhân vì sao mà chúng ta luôn cần tới verbose default getters và setter trong Java.

Vấn đề trên được giải quyết với Kotin nhờ vào việc sử dụng properties với auto-generated default getters và setters thay vì field.

class KotlinPerson {

    var name: String? = null
    var age: Int? = null
}

Nói cách khác, bạn có thể truy cập vào các properties như public fields trong Java với person.namehoặc person.age., không những thế mà ta còn thêm và custom getters và setters mà không lo API của class bị thay đổi.

class KotlinPerson {

    var name: String? = null

    var age: Int? = null
    set(value) {
        if (value in 0..120){
            field = value
        } else{
            throw IllegalArgumentException()
        }
    }
}

Tóm lại là với Kotlin properties thì ta sẽ có class với độ chuẩn xác cao mà lại cực kì linh hoạt.

Override nên là key word chính của annotation

Annotations được đưa vào Java từ bản cập nhật 1.5. trong đó có Override. Khi vào làm trong Java, bạn sẽ nhận ra rằng mình nên luôn dùng override để tránh dính phải những bug nguy hiểm. Lúc đó, complier sẽ thông báo là có error khi bạn overriding một method từ superclass, thế nhưng thật ra là không phải đâu, miễn là bạn vẫn sử dụng Override annotation thì mọi chuyện đều ổn cả.

Trong Kotlin thì Override annotation còn được xem là keyword chính luôn. Thế nên khỏi phải lo về vụ gặp phải mấy cái bug hiểm ác. Kotlin sẽ giúp bạn code nhẹ nhàng hơn rất nhiều.

Nguồn: Hackernoon

3 Likes

hi, rất vui khi nhận được bài chia sẻ của bạn. Bạn sử dụng Kotlin để làm gì thế ?

Em đang học Kotlin Android, em áp dụng Kotlink để làm app cho Android !

1 Like

Mình thì đang làm Kotlin cho backend thay thế Java :sweat_smile:

Anh có tạo dc thêm Danh mục cho forum ko ạ? tạo cho e cái danh mục Ebook Kotlin để em chia sẻ ebook kotlin nhé anh !

À mình có mục “Tài nguyên” để chứa tools , ebook, cảm ơn em nhé. Diễn đàn rất cần đóng góp từ các thành viên

https://vnkotlin.com/c/resources

Hi bạn, cảm ơn bài chia sẽ của bạn.
Mình có thắc mắc chỗ này chưa hiểu lắm. Trong ví dụ lớp KotlinPerson khi mình truy cập các properties trực tiếp như person.name . Như vậy là chưa tuân thủ nguyên tắc đóng gói của OOP phải ko nhỉ ? Và nếu mình đặt thêm modifiers cho các properties như sau

 class KotlinPerson {
     private var name: String? = null
     private var age: Int? = null
 }

Như vậy mình phải cần phải custom lại Getter và Setter. Vậy nhìn chung có vẻ không khác java là mấy nhỉ ? Bạn giúp mình làm rõ vấn đề này với. Thanks bạn

Mình nghĩ data abstraction chỉ cần áp dụng với các thuộc tính thiết yếu của hệ thống tùy vào thiết kế của bạn, khi đó bạn dùng modifier và Kotlin cũng cung cấp getter/setter , với trường hợp các class kiểu data class thì cách gán trực tiếp sẽ nhanh hơn , ví dụ như đoạn code :

val jane = User("Jane", 35) 
val (name, age) = jane
println("$name, $age years of age") // prints "Jane, 35 years of age"

Ngoài ra còn valvar sẽ giúp bạn hạn chế việc thay đổi giá trị thuộc tính hết mức có thể nữa, nhìn chung Kotlin khá là linh động

2 Likes