Truy cập các chức năng mở rộng của Kotlin từ Java


155

Có thể truy cập các chức năng mở rộng từ mã Java?

Tôi đã định nghĩa hàm mở rộng trong tệp Kotlin.

package com.test.extensions

import com.test.model.MyModel

/**
 *
 */
public fun MyModel.bar(): Int {
    return this.name.length()
}

Trường hợp MyModelmột lớp java (được tạo). Bây giờ, tôi muốn truy cập nó trong mã java bình thường của tôi:

MyModel model = new MyModel();
model.bar();

Tuy nhiên, điều đó không làm việc. IDE sẽ không nhận ra bar()phương thức và quá trình biên dịch thất bại.

Những gì công việc đang sử dụng với một chức năng tĩnh từ kotlin:

public fun bar(): Int {
   return 2*2
}

bằng cách sử dụng import com.test.extensions.ExtensionsPackageđể IDE của tôi dường như được cấu hình đúng.

Tôi đã tìm kiếm trong toàn bộ tệp Java-interop từ các tài liệu kotlin và cũng đã tìm kiếm rất nhiều, nhưng tôi không thể tìm thấy nó.

Tôi đang làm gì sai? Điều này thậm chí có thể?


Xin hãy giải thích trên không hoạt động ? Nó biên dịch, ném một ngoại lệ, hoặc những gì? Cũng có bạn import package com.test.extensions.MyModel?
meskobalazs 3/2/2015

@meskobalazs xem câu trả lời đã được chỉnh sửa của tôi.
Lovis

@meskobalazs cũng vậy, tôi thậm chí không thể nhập cái đó. Tôi chỉ có thể nhậpcom.test.extensions.ExtensionsPackage
Lovis

Câu trả lời:


229

Tất cả các hàm Kotlin được khai báo trong một tệp sẽ được biên dịch theo mặc định thành các phương thức tĩnh trong một lớp trong cùng một gói và với một tên xuất phát từ tệp nguồn Kotlin (chữ cái đầu tiên viết hoa và phần mở rộng ".kt" được thay thế bằng hậu tố "Kt" ) . Các phương thức được tạo cho các hàm mở rộng sẽ có thêm một tham số đầu tiên với loại bộ nhận hàm mở rộng.

Áp dụng nó cho câu hỏi ban đầu, trình biên dịch Java sẽ thấy tệp nguồn Kotlin với tên example.kt

package com.test.extensions

public fun MyModel.bar(): Int { /* actual code */ }

như thể lớp Java sau được khai báo

package com.test.extensions

class ExampleKt {
    public static int bar(MyModel receiver) { /* actual code */ }
}

Vì không có gì xảy ra với lớp mở rộng theo quan điểm Java, bạn không thể chỉ sử dụng cú pháp dấu chấm để truy cập các phương thức như vậy. Nhưng chúng vẫn có thể gọi được như các phương thức tĩnh Java bình thường:

import com.test.extensions.ExampleKt;

MyModel model = new MyModel();
ExampleKt.bar(model);

Nhập tĩnh có thể được sử dụng cho lớp exampleKt:

import static com.test.extensions.ExampleKt.*;

MyModel model = new MyModel();
bar(model);

1
Tôi hiểu, điều đó có nghĩa là tôi không thể sử dụng phần mở rộng từ Kotlin sang Java trừ khi tôi viết các hàm tĩnh?
AbdulMomen عبدالمؤمن

11
JFYI, Bạn cũng có thể thay đổi tên của lớp bằng @file: JvmName ("<TheNameThatYouWant> Utils").
crgarridos

3
Nếu tôi tạo một phần mở rộng với các tham số thì sao?
AmirG

3
Các tham số @AmirG sẽ theo ví dụ. Trong trường hợp này, nó sẽ làbar(model, param1, param2);
Tim

Đây thực sự là một sự trợ giúp nhanh chóng, Cảm ơn rất nhiều
Vivek Gupta

31

Hàm mở rộng cấp cao nhất của Kotlin được biên dịch thành các phương thức tĩnh Java.

Cho tập tin Kotlin Extensions.kttrong gói foo.barchứa:

fun String.bar(): Int {
    ...
}

Mã Java tương đương sẽ là:

package foo.bar;

class ExtensionsKt {
    public static int bar(String receiver) { 
        ...
    }
}

Trừ khi, có nghĩa là, Extensions.ktchứa dòng

@file:JvmName("DemoUtils")

Trong trường hợp đó, lớp tĩnh Java sẽ được đặt tên DemoUtils

Trong Kotlin, các phương thức mở rộng có thể được khai báo theo những cách khác. (Ví dụ: dưới dạng hàm thành viên hoặc dưới dạng phần mở rộng của đối tượng đồng hành.)


8

Tôi có một tệp Kotlin có tên NumberFormatted.kt có chức năng sau

fun Double.formattedFuelAmountString(): String? {
    val format = NumberFormat.getNumberInstance()
    format.minimumFractionDigits = 2
    format.maximumFractionDigits = 2
    val string = format.format(this)
    return string
}

Trong java tôi đơn giản truy cập nó qua tệp NumberFormattedKt theo cách sau sau khi nhập yêu cầu import ....extensions.NumberFormattingKt;

String literString = NumberFormattingKt.formattedFuelAmountString(item.getAmount());

6

Bạn luôn có thể thấy mã Java thực tế đang được tạo từ mã Kotlin của bạn bằng cách đi tới Tools > Kotlin > Show Kotlin Bytecode, sau đó nhấp vào Decompile. Điều này có thể giúp bạn rất nhiều. Trong trường hợp của bạn, mã Java sẽ trông như thế này nếu bạn cóMyModelExtensions.kt

public final class MyModelExtensionsKt {
   public static final int bar(@NotNull MyModel $receiver) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      return $receiver.getName().length();
   }
}

bạn có thể cải thiện vấn đề này bằng cách sử dụng @JvmNametệp có chứa bar:

@file:JvmName("MyModels")
package io.sspinc.datahub.transformation

public fun MyModel.bar(): Int {
    return this.name.length
}

và nó sẽ dẫn đến mã này:

public final class MyModels {
   public static final int bar(@NotNull MyModel $receiver) {
      Intrinsics.checkParameterIsNotNull($receiver, "$receiver");
      return $receiver.getName().length();
   }
}

Việc sử dụng MyModelsphù hợp với những gì Java hiệu quả gợi ý cho các lớp tiện ích. Bạn cũng có thể đổi tên phương thức của mình như thế này:

public fun MyModel.extractBar(): Int {
    return this.name.length
}

sau đó từ phía Java nó sẽ trông thành ngữ:

MyModels.extractBar(model);

0

Bạn cần sao chép các chức năng của mình trong các tệp lớp:

Tạo tập tin Kotlin, ví dụ như Utils.kt

Nhập mã

  class Utils {
                companion object {
                    @JvmStatic
                    fun String.getLength(): Int {//duplicate of func for java
                        return this.length
                    }
                }
            }

        fun String.getLength(): Int {//kotlin extension function
            return this.length
        }

HOẶC LÀ

class Utils {
    companion object {

        @JvmStatic
        fun getLength(s: String): Int {//init func for java
            return s.length
        }
    }
}

fun String.getLength(): Int {//kotlin extension function
    return Utils.Companion.getLength(this)//calling java extension function in Companion
}

Trong kotlin sử dụng:

val str = ""
val lenth = str.getLength()

Trong Java sử dụng điều này:

String str = "";
 Integer lenth = Utils.getLength(str);

0

Nó hoạt động với tôi:

Kotlin kotlin

Mã Java nhập mô tả hình ảnh ở đây

Dự án của tôi là một dự án Android cũ được tạo bằng Java; bây giờ tôi đã tạo tệp kotlin đầu tiên và thêm tiện ích mở rộng Chuỗi vui nhộn String.isNotNullOrEmpty (): Boolean {...}

và tôi có thể gọi nó từ tệp java bằng cách sử dụng: StringUtilsKt.isNotNullOrEmpty (thestring).

Tên tệp kotlin của tôi là StringUtils


-1

Các câu trả lời khác ở đây bao gồm trường hợp gọi một hàm mở rộng nằm ở cấp cao nhất của tệp gói Kotlin.

Tuy nhiên, trường hợp của tôi là tôi cần gọi một hàm Extension nằm trong Class. Cụ thể, tôi đã làm việc với một đối tượng.

Giải pháp cực kỳ đơn giản .

Tất cả bạn phải làm là chú thích chức năng mở rộng của bạn như @JvmStatic, và thì đấy! Mã Java của bạn sẽ có thể truy cập và sử dụng nó.


Tại sao các downvote? Câu trả lời của tôi là chính xác và nguyên bản.
forresthopkinsa

-2

Khi bạn mở rộng một lớp học như thế này:

fun String.concatenatedLength(str: String): Int {
    return (this.length + str.length)
}

fun f() {
    var len = "one string".concatenatedLength("another string")
    println(len)
}

Nó sẽ biên dịch thành này:

import kotlin.jvm.internal.Intrinsics;
import org.jetbrains.annotations.NotNull;

public final class ExampleKt {
  public static final int concatenatedLength(@NotNull String $receiver, @NotNull String str) {
    Intrinsics.checkParameterIsNotNull((Object) $receiver, (String) "$receiver");
    Intrinsics.checkParameterIsNotNull((Object) str, (String) "str");
    return $receiver.length() + str.length();
  }

  public static final void f() {
    int len = ExampleKt.concatenatedLength("one string", "another string");
    System.out.println(len);
  }
}

Có nhiều ví dụ ở đây .


-3

Theo như tôi có thể nói điều này là không thể. Từ việc tôi đọc các tài liệu mở rộng, có vẻ như

public fun MyModel.bar(): Int {
    return this.name.length()
}

tạo một phương thức mới với chữ ký

public static int MyModelBar(MyModel obj) {
    return obj.name.length();
}

Sau đó, Kotlin ánh xạ chức năng cho các cuộc gọi của biểu mẫu myModel.bar(), trong đó nếu bar()không tìm thấy trong MyModellớp, nó sẽ tìm các phương thức tĩnh khớp với chữ ký và sơ đồ đặt tên mà nó đưa ra. Lưu ý rằng đây chỉ là một giả định từ các tuyên bố của họ về các tiện ích mở rộng được nhập tĩnh và không ghi đè các phương thức được xác định. Tôi đã không đủ xa trong nguồn của họ để biết chắc chắn.

Vì vậy, giả sử những điều trên là đúng, không có cách nào để các phần mở rộng Kotlin được gọi từ mã java cũ đơn giản, vì trình biên dịch sẽ chỉ thấy một phương thức không xác định được gọi trên một đối tượng và lỗi.


3
Câu trả lời này không chính xác, chúng có thể được truy cập. Xem câu trả lời được chấp nhận.
Jayson Minard

Và trình biên dịch không tìm kiếm các phương thức tĩnh (đặc biệt là các phương thức tĩnh Java). Đó là tìm kiếm các phương thức mở rộng đã được khai báo hoặc nhập trong cùng một tệp.
Kirill Rakhman
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.