การเข้าถึงฟังก์ชั่นเสริม Kotlin จาก Java


157

เป็นไปได้หรือไม่ที่จะเข้าถึงฟังก์ชั่นส่วนขยายจากรหัส Java?

ฉันกำหนดฟังก์ชันส่วนขยายในไฟล์ Kotlin

package com.test.extensions

import com.test.model.MyModel

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

MyModelคลาส java (สร้าง) อยู่ที่ไหน ตอนนี้ฉันต้องการเข้าถึงมันในรหัส java ปกติของฉัน:

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

อย่างไรก็ตามนั่นไม่ได้ผล IDE จะไม่รู้จักbar()วิธีการและการรวบรวมล้มเหลว

สิ่งที่ใช้กับฟังก์ชั่นคงที่จาก kotlin:

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

โดยใช้import com.test.extensions.ExtensionsPackageดังนั้น IDE ของฉันดูเหมือนว่าจะกำหนดค่าอย่างถูกต้อง

ฉันค้นหาผ่านไฟล์ Java-interop ทั้งหมดจาก kotlin docs และ googled มาก แต่ฉันหามันไม่เจอ

ผมทำอะไรผิดหรือเปล่า? เป็นไปได้ไหม


กรุณาอธิบายรายละเอียดเกี่ยวกับไม่ทำงาน ? มันรวบรวมรวบรวมโยนข้อยกเว้นหรืออะไร นอกจากนี้ยังมีคุณimport package com.test.extensions.MyModel?
meskobalazs

@meskobalazs ดูคำตอบที่แก้ไขของฉัน
Lovis

@meskobalazs ด้วยฉันไม่สามารถแม้แต่จะนำเข้าสิ่งนั้นได้ ฉันสามารถนำเข้าได้com.test.extensions.ExtensionsPackage
Lovis

คำตอบ:


230

ฟังก์ชัน Kotlin ทั้งหมดที่ประกาศในไฟล์จะถูกรวบรวมโดยค่าเริ่มต้นเป็นวิธีการคงที่ในคลาสภายในแพ็คเกจเดียวกันและมีชื่อที่ได้มาจากไฟล์ต้นฉบับ Kotlin (ตัวอักษรตัวแรกพิมพ์ใหญ่และนามสกุล".kt"แทนด้วยคำต่อท้าย"Kt" ) . วิธีการที่สร้างขึ้นสำหรับฟังก์ชั่นส่วนขยายจะมีพารามิเตอร์แรกเพิ่มเติมพร้อมกับประเภทตัวรับฟังก์ชั่นส่วนขยาย

นำไปใช้กับคำถามต้นฉบับคอมไพเลอร์ Java จะเห็นไฟล์ต้นฉบับ Kotlin พร้อมชื่อexample.kt

package com.test.extensions

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

ราวกับว่ามีการประกาศคลาส Java ต่อไปนี้

package com.test.extensions

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

เนื่องจากไม่มีสิ่งใดเกิดขึ้นกับคลาสแบบขยายจากมุมมอง Java คุณไม่สามารถใช้ dot-syntax เพื่อเข้าถึงวิธีดังกล่าวได้ แต่พวกเขายังคงเรียกได้ว่าเป็นวิธีการแบบคงที่ Java ปกติ:

import com.test.extensions.ExampleKt;

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

การอิมพอร์ตแบบสแตติกสามารถใช้สำหรับคลาส ExampleKt:

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

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

1
ฉันเข้าใจแล้วหมายความว่าฉันไม่สามารถใช้ส่วนขยายจาก Kotlin ไปยัง Java ได้เว้นแต่ว่าฉันจะเขียนฟังก์ชั่นคงที่?
AbdulMomen عبدالمؤمن

11
JFYI คุณสามารถเปลี่ยนชื่อคลาสด้วย @file: JvmName ("<TheNameThatYouWant> Utils")
crgarridos

3
ถ้าฉันสร้างส่วนขยายพร้อมพารามิเตอร์
AmirG

3
พารามิเตอร์ @AmirG จะทำตามตัวอย่าง ในกรณีนี้มันจะเป็นbar(model, param1, param2);
ทิม

นี่เป็นความช่วยเหลือด่วนจริงๆขอบคุณมาก
Vivek Gupta

31

ฟังก์ชันส่วนขยายระดับบนสุดของ Kotlin ถูกคอมไพล์เป็นวิธีคงที่ของ Java

ไฟล์ Kotlin ที่ให้Extensions.ktไว้ในแพ็คเกจfoo.barที่บรรจุ:

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

โค้ด Java ที่เทียบเท่าจะเป็น:

package foo.bar;

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

ยกเว้นว่ามีExtensions.ktอยู่ในบรรทัด

@file:JvmName("DemoUtils")

ในกรณีนี้คลาส Java คงที่จะมีชื่อ DemoUtils

ใน Kotlin สามารถประกาศวิธีการขยายด้วยวิธีอื่นได้ (ตัวอย่างเช่นเป็นฟังก์ชันสมาชิกหรือเป็นส่วนขยายของวัตถุที่แสดงร่วม)


8

ฉันมีไฟล์ Kotlin ชื่อ NumberFormatting.kt ที่มีฟังก์ชั่นดังต่อไปนี้

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

ใน java ฉันเข้าถึงได้ง่ายผ่านไฟล์NumberFormattingKtด้วยวิธีต่อไปนี้หลังจากการนำเข้าที่ต้องการ import ....extensions.NumberFormattingKt;

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

6

คุณสามารถดูที่เกิดขึ้นจริงรหัส Java ซึ่งจะได้รับการสร้างขึ้นจากรหัส Kotlin ของคุณโดยไปแล้วคลิกTools > Kotlin > Show Kotlin Bytecode Decompileสิ่งนี้สามารถช่วยคุณได้อย่างมาก ในกรณีของคุณรหัส Java จะมีลักษณะเช่นนี้หากคุณมีMyModelExtensions.kt

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

คุณสามารถปรับปรุงได้โดยใช้@JvmNameไฟล์ที่มีbar:

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

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

และมันจะส่งผลให้รหัสนี้:

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

การใช้MyModelsสอดคล้องกับสิ่งที่ Java ที่มีประสิทธิภาพแนะนำสำหรับคลาสยูทิลิตี้ คุณสามารถเปลี่ยนชื่อวิธีการของคุณเช่นนี้:

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

จากนั้นในฝั่ง Java มันจะดูเป็นสำนวน:

MyModels.extractBar(model);

0

คุณต้องทำซ้ำฟังก์ชั่นของคุณในไฟล์คลาส:

สร้างไฟล์ Kotlin สำหรับ Utils.kt

ใส่รหัส

  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
        }

หรือ

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
}

ในการใช้ kotlin:

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

ใน Java ใช้สิ่งนี้:

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

0

มันเหมาะกับฉัน:

Kotlin Kotlin

รหัส Java ป้อนคำอธิบายรูปภาพที่นี่

โปรเจคของฉันเป็นโปรเจ็กต์เก่าที่สร้างด้วย Java; ตอนนี้ฉันสร้างไฟล์ kotlin แรกและเพิ่มความสนุกของส่วนขยายสตริง String.isNotNullOrEmpty (): Boolean {... }

และฉันสามารถเรียกมันจากไฟล์ java โดยใช้: StringUtilsKt.isNotNullOrEmpty (thestring)

ชื่อไฟล์ kotlin ของฉันคือ StringUtils


-1

คำตอบอื่น ๆ ที่นี่ครอบคลุมกรณีของการเรียกใช้ฟังก์ชันส่วนขยายที่อยู่ที่ระดับบนสุดของไฟล์แพ็คเกจ Kotlin

อย่างไรก็ตามกรณีของฉันคือฉันต้องเรียกใช้ฟังก์ชันส่วนขยายที่อยู่ภายใน Class โดยเฉพาะฉันกำลังจัดการกับวัตถุ

การแก้ปัญหาคือง่ายอย่างเหลือเชื่อ

สิ่งที่คุณต้องทำคือใส่คำอธิบายประกอบการทำงานของส่วนขยายของคุณเป็น@JvmStaticและ voila! รหัส Java ของคุณจะสามารถเข้าถึงและใช้งานได้


ทำไมต้องลงคะแนน? คำตอบของฉันถูกต้องและเป็นต้นฉบับ
forresthopkinsa

-2

เมื่อคุณขยายชั้นเรียนเช่นนี้:

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

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

มันจะรวบรวมสิ่งนี้:

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);
  }
}

มีมากขึ้นเป็นตัวอย่างที่นี่


-3

เท่าที่ฉันสามารถบอกได้ว่ามันเป็นไปไม่ได้ จากการอ่านเอกสารส่วนขยายของฉันฉันพบว่า

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

สร้างวิธีการใหม่ด้วยลายเซ็น

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

จากนั้น Kotlin จะแมปฟังก์ชั่นนั้นกับการเรียกแบบฟอร์มmyModel.bar()ซึ่งหากbar()ไม่พบในMyModelชั้นเรียนจะค้นหาวิธีการคงที่ที่ตรงกับลายเซ็นและรูปแบบการตั้งชื่อที่มันส่งออกโปรดทราบว่านี่เป็นเพียงข้อสันนิษฐานจากคำแถลงเกี่ยวกับส่วนขยายที่นำเข้าแบบสแตติกและไม่แทนที่วิธีที่กำหนดไว้ ฉันไม่ได้รู้แหล่งที่มาของพวกเขามากพอ

ดังนั้นสมมติว่าข้างต้นเป็นความจริงไม่มีทางที่นามสกุล Kotlin จะถูกเรียกจากโค้ดจาวาแบบเก่าธรรมดาเนื่องจากคอมไพเลอร์จะเห็นวิธีที่ไม่รู้จักที่ถูกเรียกบนวัตถุและข้อผิดพลาด


3
คำตอบนี้ไม่ถูกต้องพวกเขาสามารถเข้าถึงได้ ดูคำตอบที่ยอมรับได้
Jayson Minard

และคอมไพเลอร์ไม่ได้มองหาวิธีการคงที่ (โดยเฉพาะอย่างยิ่งไม่ใช่วิธีการคงที่ของ Java) กำลังมองหาวิธีการขยายที่มีการประกาศหรือนำเข้าในไฟล์เดียวกัน
คิริลล์ Rakhman
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.