Kotlin: Giao diện không có nhà xây dựng


138

Tôi đang chuyển đổi một số mã Java của mình sang Kotlin và tôi hoàn toàn không hiểu làm thế nào để khởi tạo các giao diện được xác định trong mã Kotlin. Ví dụ, tôi có một giao diện (được xác định trong mã Java):

public interface MyInterface {
    void onLocationMeasured(Location location);
}

Và sau đó trong mã Kotlin của tôi, tôi khởi tạo giao diện này:

val myObj = new MyInterface { Log.d("...", "...") }

và nó hoạt động tốt. Tuy nhiên, khi tôi chuyển đổi MyInterface thành Kotlin:

interface MyInterface {
    fun onLocationMeasured(location: Location)
}

Tôi nhận được một thông báo lỗi: Interface MyListener does not have constructorskhi tôi cố gắng khởi tạo nó - mặc dù với tôi dường như không có gì thay đổi ngoại trừ cú pháp. Tôi có hiểu nhầm cách các giao diện hoạt động trong Kotlin không?

Câu trả lời:


225

Mã Java của bạn dựa trên chuyển đổi SAM - chuyển đổi tự động lambda thành giao diện với một phương thức trừu tượng duy nhất. Chuyển đổi SAM hiện không được hỗ trợ cho các giao diện được xác định trong Kotlin. Thay vào đó, bạn cần xác định một đối tượng ẩn danh thực hiện giao diện:

val obj = object : MyInterface {
    override fun onLocationMeasured(location: Location) { ... }
}

14
Cảm ơn nhiều. Từ liên kết bạn đã đăng, tôi hiểu rằng nên sử dụng các loại chức năng (ví dụ Location -> Unit) thay vì giao diện phương thức đơn nếu có thể - có đúng không?
Aleph Aleph

4
Đung vậy. Bạn nên sử dụng loại chức năng bất cứ nơi nào có thể.
Yoav Sternberg

Tuy nhiên, trong trường hợp của tôi, giao diện (SurfaceTextureListener) có nhiều phương thức.
Tash Pemhiwa

Cám ơn rất nhiều. Câu trả lời này phải có nhiều lượt thích hoặc một số dấu hiệu đặc biệt, vì đó là thông tin rất hữu ích và thật không may, một số người học có thể trở nên rất bối rối khi tìm hiểu về Kotlin bởi các bài báo hoặc bởi "Kotlin in Action" khi họ nhìn vào chủ đề SAM.
TT_W

từ "Kotlin in Action", vâng, bạn có thể sử dụng lambda trong thông số SAM của Java để rút ngắn và mã sạch hơn, nhưng không phải SAM của Kotlin, loại chức năng là lớp đầu tiên trong Kotlin, vì vậy, SAM không có nghĩa đối với Kotlin, loại chức năng với kiểu chữ là phong cách hơn Kotlin.
vg0x00

17

Giải pháp tốt nhất là sử dụng một kiểu chữ thay cho giao diện Java của bạn

typealias MyInterface = (Location) -> Unit

fun addLocationHandler(myInterface:MyInterface) {

}

Đăng ký nó như thế này:

val myObject = { location -> ...}
addLocationHandler(myObject)

hoặc thậm chí sạch hơn

addLocationHandler { location -> ...}

Gọi nó như thế này:

myInterface.invoke(location)

3 tùy chọn hiện tại dường như là:

  • typealias (lộn xộn khi được gọi từ java)
  • Giao diện kotlin (lộn xộn khi được gọi từ kotlin; bạn cần tạo một đối tượng) Đây là một bước lùi lớn IMO.
  • Giao diện java (ít lộn xộn hơn khi được gọi từ kotlin; lambda cần tên giao diện được đặt trước để bạn không cần một đối tượng; cũng không thể sử dụng lambda ngoài quy ước dấu ngoặc đơn của hàm)

Khi chuyển đổi thư viện của chúng tôi sang Kotlin, chúng tôi thực sự đã để lại tất cả các giao diện trong mã Java, vì việc gọi Java từ Kotlin sẽ tốt hơn so với Kotlin từ Kotlin.


8

Hãy thử truy cập vào giao diện của bạn như thế này:

 object : MyInterface {
    override fun onSomething() { ... }
}

6

nếu bạn có lớp Java như thế này:

recyclerView.addOnItemTouchListener(new RecyclerTouchListener(getActivity(), recyclerView, new RecyclerTouchListener.ClickListener()
        {
              //Your Code
        }));

bạn nên chuyển đổi mã này từ Java sang Kotlin như thế này:

override fun showJozList (list : List<ResponseGetJuzList.Parameter4>) {
        adapter.addData(list)
        jozlist_recycler.addOnItemTouchListener(RecyclerTouchListener(
                activity ,
                jozlist_recycler ,
                object : RecyclerTouchListener.ClickListener
                    {
                          //Your Code
                    }))

chuyển đổi giao diện Java :

new RecyclerTouchListener.ClickListener()

đến Phong cách giao diện của Kotlin :

object : RecyclerTouchListener.ClickListener

1

Nếu giao diện dành cho phương thức nghe của một lớp, hãy thay đổi định nghĩa giao diện thành loại hàm. Điều đó làm cho mã ngắn gọn hơn. Xem những điều sau đây.

Lớp chứa định nghĩa người nghe

// A class
private var mLocationMeasuredListener = (location: Location) -> Unit = {}

var setOnLocationMeasuredListener(listener: (location: Location) -> Unit) {
    mLocationMeasuredListener = listener
}

// somewhere in A class
mLocationMeasuredListener(location)

Một lớp khác

// B class
aClass.setOnLocationMeasuredListener { location ->
    // your code
}

-1
class YourClass : YourInterface {  
    override fun getTest() = "test"    
}

interface YourInterface {
    fun getTest(): String
}

val objectYourClass: YourInterface = YourClass()
print(objectYourClass.getTest())
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.