Khởi tạo biến Kotlin cho lớp con hoạt động kỳ lạ khi khởi tạo biến có giá trị 0


16

Tôi đã tạo ra hệ thống phân cấp lớp sau:

open class A {
    init {
        f()
    }

    open fun f() {
        println("In A f")
    }
}

class B : A() {
    var x: Int = 33

    init {
        println("x: " + x)
    }

    override fun f() {
        x = 1
        println("x in f: "+ x)
    }

    init {
        println("x2: " + x)
    }
}

fun main() {
    println("Hello World!!")
    val b = B()
    println("in main x : " + b.x)
}

Đầu ra của mã này là

Hello World!!
x in f: 1
x: 33
x2: 33
in main x : 33

Nhưng nếu tôi thay đổi việc khởi tạo xtừ

var x: Int = 33

đến

var x: Int = 0

đầu ra cho thấy lời gọi của phương thức trái ngược với đầu ra ở trên:

Hello World!!
x in f: 1
x: 1
x2: 1
in main x : 1

Có ai biết tại sao việc khởi tạo với 0một hành vi khác với hành vi có giá trị khác không?


4
Không liên quan trực tiếp, nhưng gọi các phương thức overridable từ các nhà xây dựng nói chung không phải là một cách thực hành tốt vì nó có thể dẫn đến hành vi không mong muốn (và phá vỡ hợp đồng / bất biến siêu lớp từ các lớp con).
Adam Hošek

Câu trả lời:


18

siêu lớp được khởi tạo trước lớp phụ.

Lệnh gọi hàm tạo của B gọi hàm tạo của A, gọi hàm f in "x in f: 1", sau khi A được khởi tạo, phần còn lại của B được khởi tạo.

Vì vậy, về cơ bản, cài đặt của giá trị đang bị ghi đè.

(Khi bạn khởi tạo nguyên thủy với giá trị 0 của chúng trong Kotlin, về mặt kỹ thuật, chúng hoàn toàn không khởi tạo)

Bạn có thể quan sát hành vi "ghi đè" này bằng cách thay đổi chữ ký từ

var x: Int = 0 đến var x: Int? = 0

xkhông còn là nguyên thủy int, trường thực sự được khởi tạo thành một giá trị, tạo ra đầu ra:

Hello World!!
x in f: 1
x: 0
x2: 0
in main x : 0

5
Khi bạn khởi tạo các nguyên thủy với giá trị bằng 0 của chúng trong Kotlin, về mặt kỹ thuật chúng chỉ không khởi tạo chút nào là những gì tôi muốn đọc ... Cảm ơn!
deHaar

Điều này vẫn có vẻ như một lỗi / không nhất quán.
Kroppeb

2
@Kroppeb đây chỉ là Java, hành vi tương tự có thể được quan sát chỉ trong mã Java. Nó không liên quan gì đến Kotlin
Sxtanna

8

Hành vi này được mô tả trong tài liệu - https://kotlinlang.org/docs/reference/groupes.html#deriving- class -initialization - order

Nếu bất kỳ thuộc tính nào được sử dụng trong logic khởi tạo lớp cơ sở (trực tiếp hoặc gián tiếp, thông qua việc thực hiện thành viên mở bị ghi đè khác), nó có thể dẫn đến hành vi không chính xác hoặc lỗi thời gian chạy. Do đó, khi thiết kế một lớp cơ sở, bạn nên tránh sử dụng các thành viên mở trong các hàm tạo, khởi tạo thuộc tính và các khối init.

CẬP NHẬT:

Có một lỗi tạo ra sự không nhất quán này - https://youtrack.jetbrains.com/su/KT-15642

Khi một thuộc tính được gán làm hiệu ứng phụ của một lệnh gọi hàm ảo bên trong siêu cấu trúc, trình khởi tạo của nó không ghi đè lên thuộc tính nếu biểu thức khởi tạo là giá trị mặc định kiểu (null, số 0 nguyên thủy).


1
Hơn nữa, IntelliJ cảnh báo bạn về điều đó. Gọi f()trong initkhối Ađưa ra cảnh báo "Gọi hàm không phải là f cuối cùng trong hàm tạo"
Kroppeb

Trong tài liệu bạn cung cấp, nó nói "việc khởi tạo lớp cơ sở được thực hiện như bước đầu tiên và do đó xảy ra trước khi logic khởi tạo của lớp dẫn xuất được chạy" , đó chính xác là những gì xảy ra trong ví dụ đầu tiên trong câu hỏi. Tuy nhiên, trong ví dụ thứ hai, hướng dẫn khởi tạo ( var x: Int = 0) của lớp dẫn xuất hoàn toàn không chạy, điều này trái với những gì tài liệu nói khiến tôi tin rằng đây có thể là một lỗi.
Subaru Tashiro

@SubaruTashiro Vâng, bạn nói đúng. Đây là một vấn đề khác - youtrack.jetbrains.com/su/KT-15642 .
vanyochek
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.