Trường sao lưu Kotlin để làm gì?


92

Là một nhà phát triển Java, khái niệm về trường hỗ trợ hơi xa lạ với tôi. Được:

class Sample {
    var counter = 0 // the initializer value is written directly to the backing field
    set(value) {
        if (value >= 0) field = value
    }
}

Trường hỗ trợ này tốt cho điều gì? Kotlin docs cho biết:

Các lớp trong Kotlin không thể có trường. Tuy nhiên, đôi khi cần phải có trường hỗ trợ khi sử dụng các trình truy cập tùy chỉnh .

Tại sao? Sự khác biệt với việc sử dụng chính tên thuộc tính bên trong bộ cài đặt, ví dụ: *

class Sample {        
    var counter = 0
    set(value) {
        if (value >= 0) this.counter = value // or just counter = value?
    }
}

17
Việc sử dụng bản thân thuộc tính trong bộ thiết lập sẽ dẫn đến một đệ quy vô tận vì việc gán một số giá trị cho thuộc tính sẽ luôn gọi trình thiết lập.
Mushlejunk

1
@Strelok my bad .... Tôi đã giả định rằng điều đó this.counter = valuegiống với Java tương đương khi đọc tài liệu của Kotlin.
Yudhistira Arya

3
Bài viết này dành cho Field vs Property.
avinash

Câu trả lời:


86

Bởi vì, giả sử nếu bạn không có fieldtừ khóa, bạn sẽ không thể thực sự đặt / nhận giá trị trong get()hoặcset(value) . Nó cho phép bạn truy cập trường hỗ trợ trong các trình truy cập tùy chỉnh.

Đây là mã Java tương đương của mẫu của bạn:

class Sample {
    private int counter = 0;
    public void setCounter(int value) {
        if (value >= 0) setCounter(value);
    }
    public int getCounter() {
        return counter;
    }
}

Rõ ràng điều này là không tốt, vì setter chỉ là một phép đệ quy xâm nhập vào chính nó, không bao giờ thay đổi bất cứ điều gì. Hãy nhớ trong kotlin bất cứ khi nào bạn viết foo.bar = valuenó sẽ được dịch thành lời gọi setter thay vì a PUTFIELD.


CHỈNH SỬA: Java có các trường trong khi Kotlin có các thuộc tính , đây là một khái niệm cấp cao hơn các trường.

Có hai loại thuộc tính: một loại có trường hỗ trợ, một loại không có.

Thuộc tính có trường hỗ trợ sẽ lưu trữ giá trị dưới dạng một trường. Trường đó giúp lưu trữ giá trị trong bộ nhớ. Một ví dụ về thuộc tính đó là thuộc tính firstsecondthuộc tính của Pair. Thuộc tính đó sẽ thay đổi biểu diễn trong bộ nhớ của Pair.

Thuộc tính không có trường hỗ trợ sẽ phải lưu trữ giá trị của chúng theo những cách khác ngoài việc lưu trực tiếp vào bộ nhớ. Nó phải được tính toán từ các thuộc tính khác, hoặc từ chính đối tượng. Một ví dụ về thuộc tính như vậy là thuộc tính indicesmở rộng của List, không được hỗ trợ bởi một trường, mà là một kết quả được tính toán dựa trên thuộc sizetính. Vì vậy, nó sẽ không thay đổi biểu diễn trong bộ nhớ List(điều mà nó không thể làm được vì Java được nhập tĩnh).


Cảm ơn vì câu trả lời! Điều tồi tệ của tôi ... Tôi đã giả định rằng điều đó this.counter = valuecũng giống với java tương đương.
Yudhistira Arya

Có tài liệu nào không? Cảm ơn :)
Alston


1
Rất nhiều lời giải thích mờ nhạt. Tại sao chúng ta không thể nói rõ ràng rằng a fieldgiống như một con trỏ hơn hoặc một tham chiếu đến một biến thành viên hiện có. Vì get/setngay sau counterđó, fieldtừ khóa là một tham chiếu đến counter. Đúng?
eigenfield

@typelogic câu trả lời này phù hợp nhất cho các lập trình viên có nền tảng Java / JS (trước đó không có Kotlin / Native), không phải C / C ++. Những gì bạn thấy mờ nhạt là bánh mì và bơ cho một số người khác.
glee8e

31

Ban đầu, tôi cũng gặp khó khăn khi hiểu khái niệm này. Vì vậy, hãy để tôi giải thích nó cho bạn với sự giúp đỡ của một ví dụ.

Hãy xem xét lớp Kotlin này

class DummyClass {
    var size = 0;
    var isEmpty
        get() = size == 0
        set(value) {
            size = size * 2
        }
}

Bây giờ khi chúng ta nhìn vào mã, chúng ta có thể thấy rằng nó có 2 thuộc tính tức là - size(với trình truy cập mặc định) và isEmpty(với trình truy cập tùy chỉnh). Nhưng nó chỉ có 1 trường tức là size. Để hiểu rằng nó chỉ có 1 trường, chúng ta hãy xem Java tương đương với lớp này.

Đi tới Công cụ -> Kotlin -> Hiển thị Kotlin ByteCode trong Android Studio. Nhấp vào Decompile.

   public final class DummyClass {
   private int size;

   public final int getSize() {
      return this.size;
   }

   public final void setSize(int var1) {
      this.size = var1;
   }

   public final boolean isEmpty() {
      return this.size == 0;
   }

   public final void setEmpty(boolean value) {
      this.size *= 2;
   }
}

Rõ ràng, chúng ta có thể thấy rằng lớp java chỉ có các hàm getter và setter isEmpty, và không có trường nào được khai báo cho nó. Tương tự trong Kotlin, không có trường hỗ trợ cho thuộc tính isEmpty, vì thuộc tính hoàn toàn không phụ thuộc vào trường đó. Do đó không có trường hỗ trợ.


Bây giờ chúng ta hãy xóa getter tùy chỉnh và setter của thuộc isEmptytính.

class DummyClass {
    var size = 0;
    var isEmpty = false
}

Và Java tương đương với lớp trên là

public final class DummyClass {
   private int size;
   private boolean isEmpty;

   public final int getSize() {
      return this.size;
   }

   public final void setSize(int var1) {
      this.size = var1;
   }

   public final boolean isEmpty() {
      return this.isEmpty;
   }

   public final void setEmpty(boolean var1) {
      this.isEmpty = var1;
   }
}

Ở đây chúng ta thấy cả các trường sizeisEmpty. isEmptylà một trường hỗ trợ vì getter và setter cho isEmptythuộc tính phụ thuộc vào nó.


4
Lời giải thích hay. Cảm ơn bạn
Sonu Sanjeev

1
Thực sự, cảm ơn vì lời giải thích. Tôi cũng đã đến với Kotlin từ Java, và khái niệm về thuộc tính là mới đối với tôi. Nhưng tôi đã hiểu nó, nhờ bạn và hướng dẫn. :)
Yamashiro Rion

Chúa có thể ban phước cho bạn.
Andrea Cioccarelli

Tôi thích câu trả lời này nó được trích dẫn trung thực các sự kiện. Tôi vẫn đang nghi ngờ, vì C # không cần fieldtừ khóa, liệu rằng một cải tiến ngôn ngữ của Kotlin sẽ loại bỏ fieldtừ khóa kỳ lạ này và tránh cho những linh hồn bơ vơ rơi xuống vực thẳm của đệ quy vô hạn?
eigenfield

9

Các trường sao lưu rất tốt để chạy xác thực hoặc kích hoạt các sự kiện khi thay đổi trạng thái. Hãy nghĩ về những lần bạn đã thêm mã vào bộ cài đặt / getter Java. Các trường sao lưu sẽ hữu ích trong các trường hợp tương tự. Bạn sẽ sử dụng các trường hỗ trợ khi bạn cần để kiểm soát hoặc có khả năng hiển thị trên setters / getters.

Khi chỉ định trường với chính tên trường, bạn thực sự đang gọi bộ thiết lập (tức là set(value)). Trong ví dụ bạn có, this.counter = valuesẽ đệ quy thành set (value) cho đến khi chúng ta làm tràn ngăn xếp của mình. Sử dụng fieldbỏ qua mã setter (hoặc getter).


Xin lỗi, nhưng giải thích của bạn có chứa thuật ngữ cần được giải thích. Và sau đó, lần đầu tiên bạn trích dẫn một kịch bản Java và sau đó đột nhiên mà không có cảnh báo, bạn chuyển lợi ích sang một câu lệnh Kotlin thực tế. Kotlin cần cho từ khóa fieldkhông có trong C # vì vậy chúng tôi cần một lời giải thích tốt hơn câu bạn đã trích dẫn ở đây.
eigenfield

2

Sự hiểu biết của tôi là sử dụng mã định danh trường làm tham chiếu đến giá trị của thuộc tính trong get hoặc set , khi bạn muốn thay đổi hoặc sử dụng giá trị của thuộc tính trong get hoặc set .

Ví dụ:

class A{
    var a:Int=1
        get(){return field * 2}    // Similiar to Java: public int geta(){return this.a * 2}
        set(value) {field = value + 1}
}

Sau đó:

var t = A()
println(t.a)    // OUTPUT: 2, equal to Java code: println(t.a * 2)
t.a = 2         // The real action is similar to Java code: t.a = t.a +1
println(t.a)    // OUTPUT: 6, equal to Java code: println(t.a * 2)

0

Thuật ngữ backing fieldnày chứa đầy bí ẩn. Từ khóa được sử dụng là field. Các get/setphương thức, nằm ngay bên cạnh biến thành viên sắp được lấy hoặc thiết lập thông qua cơ chế phương thức bảo vệ cửa này. Các fieldtừ khóa chỉ đề cập đến các biến thành viên đó sẽ được thiết lập hoặc có được . Kotlin hiện tại, bạn không thể tham chiếu đến biến thành viên trực tiếp bên trong các phương thức cửa bảo vệ get hoặc set vì nó sẽ không may dẫn đến đệ quy vô hạn vì nó sẽ gọi lại get hoặc set và do đó dẫn thời gian chạy xuống vực sâu.

Tuy nhiên, trong C # , bạn có thể tham chiếu trực tiếp biến thành viên bên trong các phương thức getter / setter. Tôi trích dẫn so sánh này để trình bày ý tưởng rằng fieldtừ khóa này là cách Kotlin hiện tại đang triển khai nó nhưng tôi hy vọng nó sẽ bị loại bỏ trong các phiên bản sau và cho phép chúng tôi tham chiếu trực tiếp trực tiếp đến biến thành viên mà không dẫn đến đệ quy vô hạn.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.