Các phương thức mở rộng tĩnh trong Kotlin


142

Làm thế nào để bạn xác định một phương thức mở rộng tĩnh trong Kotlin? Điều này thậm chí có thể? Tôi hiện đang có một phương pháp mở rộng như hình dưới đây.

public fun Uber.doMagic(context: Context) {
    // ...
}

Phần mở rộng trên có thể được gọi trên một ví dụ.

uberInstance.doMagic(context) // Instance method

nhưng làm thế nào để tôi làm cho nó phương thức tĩnh như hiển thị bên dưới.

Uber.doMagic(context)         // Static or class method

1
"Phương pháp mở rộng tĩnh" nghĩa là gì?
Andrey Breslav

3
Một phương thức mà tôi có thể gọi mà không cần một thể hiện, nhưng vẫn là một phần mở rộng của lớp. (Các phương thức tĩnh Java thông thường)
Ragunath Jawahar

Tôi nghĩ rằng nó có thể không phải là mục đích sử dụng của phần mở rộng Kotlin , tôi nghĩ về điều tương tự trong khi cố gắng ngoại suy khái niệm C #. Nhưng trong khi thực tế việc sử dụng khá giống nhau. Các phần mở rộng của Kotlin, trong khi chúng được tuyên bố là được gửi tĩnh, chúng có cảm giác như được "đính kèm" một cách linh hoạt, nếu có một điều như vậy, hoặc bị ràng buộc muộn vào trường hợp. Nếu tôi không nhầm ... hoặc có lẽ tôi đã hiểu sai hoàn toàn :)
Dan

7
Hiện tại bạn không thể viết một phương thức mở rộng sẽ được gọi trên một tên lớp trái ngược với thể hiện của lớp. Thật vậy, hàm mở rộng lấy tham số máy thu là một thể hiện, vì vậy không có cách nào để bỏ qua phần này. Mặt khác, chúng tôi đang nghiên cứu cách viết phần mở rộng cho các đối tượng lớp, để bạn gọi nó bằng tên lớp, nhưng người nhận có loại đối tượng lớp
Andrey Breslav

@AndreyBreslav Bạn có thể cập nhật cho chúng tôi xem các chức năng mở rộng tĩnh có được phép không? Tôi cũng đang nhớ nó
Lars Blumberg

Câu trả lời:


143

Để đạt được Uber.doMagic(context), bạn có thể viết một phần mở rộng cho đối tượng đồng hành của Uber(bắt buộc phải khai báo đối tượng đồng hành):

class Uber {
    companion object {}
}

fun Uber.Companion.doMagic(context: Context) { }

79
Điều này thật tuyệt nhưng nếu đó là lớp Java hoặc lớp không có bạn đồng hành thì sao?
Jire

31
@Jire cho những trường hợp như vậy, không có hỗ trợ nào trong Kotlin 1.0
Andrey Breslav

2
Tôi có thể thấy một usecase trong việc mở rộng các lớp khung JVM với các giá trị mặc định, ví dụ String.DEFAULT hoặc Int.DEFAULT, trong trường hợp logic miền của bạn yêu cầu điều này (hiện tại tôi kết hợp với các lớp dữ liệu trống).
Steffen Funke

36
Nó chỉ hoạt động miễn là Uber là một lớp Kotlin . Sẽ rất tốt nếu có khả năng mở rộng tĩnh các lớp Java
kosiara - Bartosz Kosarzycki

6
Đây vẫn là không tốt khi bạn có thể xem tại đây: youtrack.jetbrains.com/issue/KT-11968 Có một SO chủ đề liên quan ở đây: stackoverflow.com/questions/33911457/...
narko

12

Đây là những gì tài liệu chính thức nói:

Kotlin tạo các phương thức tĩnh cho các hàm cấp gói. Kotlin cũng có thể tạo các phương thức tĩnh cho các hàm được xác định trong các đối tượng được đặt tên hoặc các đối tượng đồng hành nếu bạn chú thích các hàm đó là @JvmStatic. Ví dụ:

Phương pháp tĩnh của Kotlin

class C {
  companion object {
    @JvmStatic fun foo() {}
    fun bar() {}
  }
}

Bây giờ, foo () là tĩnh trong Java, trong khi thanh () thì không:

C.foo(); // works fine
C.bar(); // error: not a static method

8

Tôi thực sự đã có câu hỏi chính xác này 30 phút trước, vì vậy tôi bắt đầu đào bới và không thể tìm thấy bất kỳ giải pháp hay cách giải quyết nào cho việc này, NHƯNG trong khi tìm kiếm tôi đã tìm thấy phần này trên trang web của Kotlinglang nói rằng:

Lưu ý rằng tiện ích mở rộng có thể được xác định bằng loại máy thu không thể nhận được. Các phần mở rộng như vậy có thể được gọi trên một biến đối tượng ngay cả khi giá trị của nó là null.

Vì vậy, sau đó tôi đã có ý tưởng điên rồ nhất, tại sao không định nghĩa một hàm mở rộng với một bộ thu nullable (mà không thực sự sử dụng bộ thu đó) và sau đó gọi nó trên một đối tượng null! Vì vậy, tôi đã thử nó, và nó hoạt động khá tốt, nhưng nó trông rất xấu xí. Nó là như thế này:

(null as Type?).staticFunction(param1, param2)

Vì vậy, tôi đã khắc phục điều đó bằng cách tạo một val trong tệp tiện ích mở rộng thuộc loại máy thu có giá trị null và sau đó sử dụng nó trong lớp khác của tôi. Vì vậy, như một ví dụ, đây là cách tôi triển khai chức năng mở rộng "tĩnh" cho Navigationlớp trong Android: Trong tệp NavigationExtensions.kt của tôi:

val SNavigation: Navigation? = null
fun Navigation?.createNavigateOnClickListener(@IdRes resId: Int, args: Bundle? = null, navOptions: NavOptions? = null,
                                                navigationExtras: Navigator.Extras? = null) : (View) -> Unit {
    //This is just implementation details, don't worry too much about them, just focus on the Navigation? part in the method declaration
    return { view: View -> view.navigate(resId, args, navOptions, navigationExtras) }
}

Trong mã sử dụng nó:

SNavigation.createNavigateOnClickListener(R.id.action_gameWonFragment_to_gameFragment)

Rõ ràng, đây không phải là một tên lớp, nó chỉ là một biến của loại lớp có giá trị null. Điều này rõ ràng là xấu về phía nhà sản xuất tiện ích mở rộng (vì họ phải tạo biến) và về phía nhà phát triển (vì họ phải sử dụng STypeđịnh dạng thay vì tên lớp thực tế), nhưng đây là cách gần nhất có thể đạt được ngay bây giờ so với các hàm tĩnh thực tế. Hy vọng, các nhà sản xuất ngôn ngữ Kotlin sẽ phản hồi vấn đề đã được tạo và thêm tính năng đó vào ngôn ngữ.


2
Hacky này có thể là. Nhưng cũng thông minh ngầu. Đây là câu trả lời sâu sắc nhất cho câu hỏi. Kotlin đang làm tốt nhất có thể để giả mạo cung cấp các tiện ích mở rộng hạng nhất, vì vậy IMO, bạn sử dụng các giải pháp tốt nhất có thể để đối phó với sự vô sinh của nó. Đẹp.
Travis Griggs

5

Bạn có thể tạo một phương thức tĩnh bằng cách sử dụng đối tượng Đồng hành như:

class Foo {
    // ...
    companion object {
        public fun bar() {
            // do anything
        }
    }
}

và sau đó bạn có thể gọi nó như:

class Baz {
    // ...
    private fun callBar() {
        Foo.bar()
    }
}

Tôi tin rằng đó không thực sự là tĩnh. Các đối tượng đồng hành cho phép bạn gọi bằng tên lớp, nhưng vẫn sử dụng một thể hiện của lớp. Ngoài ra, câu hỏi là về các hàm mở rộng tĩnh, không chỉ các hàm tĩnh. Tuy nhiên, để có các hàm tĩnh, bạn có thể sử dụng platformStaticchú thích.
Ragunath Jawahar

1
platformStaticđã được đổi tên JvmStatictrong Kotlin hiện tại.
Jayson Minard

0

Giới thiệu bạn để xem liên kết này . Như bạn có thể thấy ở đó, bạn chỉ cần khai báo phương thức ở cấp cao nhất của gói (tệp):

package strings
public fun joinToString(...): String { ... }

Điều này tương đương với

package strings;

public class JoinKt {
    public static String joinToString(...) { ... }
}

Với constans mọi thứ đều giống nhau. Tuyên bố này

val UNIX_LINE_SEPARATOR = "\n"

bằng

public static final String UNIX_LINE_SEPARATOR = "\n";

-3

Tôi cũng khá thích có khả năng thêm các phương thức mở rộng tĩnh trong Kotlin. Như một giải pháp cho bây giờ tôi đang thêm phương thức exntension vào nhiều lớp thay vì sử dụng một phương thức mở rộng tĩnh trong tất cả chúng.

class Util    

fun Util.isDeviceOnline(context: Context): Boolean {
    val connMgr = context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
    val networkInfo = connMgr.activeNetworkInfo
    return networkInfo != null && networkInfo.isConnected
}

fun Activity.isDeviceOnline(context: Context) = { Util().isDeviceOnline(context) }
fun OkHttpClient.isDeviceOnline(context: Context) = { Util().isDeviceOnline(context) }

2
Câu hỏi ban đầu là về cách một javaclass có thể được mở rộng bằng một phương thức tĩnh . Trong trường hợp của bạn, điều đó có nghĩa là có thể gọi theo nghĩa đen Activity.isDeviceOnline(...)mà không cần có một trường hợp Activity.
Fabian Zeindl

-4

Để tạo một phương thức mở rộng trong kotlin, bạn phải tạo một tệp kotlin (không phải là một lớp) sau đó khai báo phương thức của bạn trong tệp Eg:

public fun String.toLowercase(){
    // **this** is the string object
}

Nhập hàm trong lớp hoặc tệp bạn đang làm việc và sử dụng nó.


Tiêu đề thậm chí không phải là một câu hỏi
daniel.jbatiz
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.