코 틀린의 정적 확장 방법


142

Kotlin에서 정적 확장 방법을 어떻게 정의합니까? 이것도 가능합니까? 현재 아래와 같이 확장 방법이 있습니다.

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

위의 확장은 인스턴스에서 호출 할 수 있습니다.

uberInstance.doMagic(context) // Instance method

그러나 아래에 표시된 것처럼 정적 방법을 어떻게 만들 수 있습니까?

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

1
"정적 확장 방법"은 무엇을 의미합니까?
Andrey Breslav

3
인스턴스없이 호출 할 수 있지만 여전히 클래스의 확장 인 메소드입니다. (일반 Java 정적 메소드)
Ragunath Jawahar

나는 그것이 Kotlin 확장 프로그램 의 의도 된 사용법이 아닐 수도 있다고 생각 합니다 .C # 개념을 외삽하려고하는 동안 같은 것에 대해 생각했습니다. 그러나 실제로 사용법은 매우 유사합니다. Kotlin 익스텐션은 정적으로 디스패치되었다고 주장하지만 그러한 것이 있거나 인스턴스에 늦게 묶여 있으면 동적으로 "부착 된"느낌이 듭니다. 내가 틀리지 않았다면 ... 아마도 완전히 틀렸다 :)
Dan

7
현재 클래스 인스턴스와 달리 클래스 이름에서 호출되는 확장 메서드를 작성할 수 없습니다. 실제로 확장 함수 는 인스턴스 인 수신자 매개 변수 를 사용하므로이 부분을 건너 뛸 수있는 방법이 없습니다. 다른 한편으로, 우리는 클래스 객체에 대한 확장을 작성하는 방법을 연구하고 있으므로 클래스 이름으로 호출하지만 수신자는 클래스 객체의 유형
을가집니다

@AndreyBreslav 정적 확장 기능 허용 여부를 업데이트 할 수 있습니까? 나는 그것을 놓쳤다.
Lars Blumberg

답변:


143

를 달성하려면 Uber.doMagic(context)다음과 같이 컴패니언 객체 에 대한 확장을 작성할 수 있습니다 Uber(컴패니언 객체 선언이 필요함).

class Uber {
    companion object {}
}

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

79
이것은 좋지만 Java 클래스이거나 동반자가없는 클래스라면 어떨까요?
Jire

31
이러한 경우에 대한 @Jire, Kotlin 1.0에서는 지원되지 않습니다
Andrey Breslav

2
도메인 로직에 요구되는 경우를 위해 String.DEFAULT 또는 Int.DEFAULT와 같은 기본값으로 JVM 프레임 워크 클래스를 확장하는 유스 케이스를 볼 수 있습니다 (현재 빈 데이터 클래스와 함께 광산이 수행함).
Steffen Funke

36
UberKotlin 클래스 인 경우에만 작동합니다 . Java 클래스도 정적으로 확장 할 수있는 가능성이 있다면 좋을 것입니다.
kosiara-Bartosz Kosarzycki

6
: 당신이 여기에서 볼 수 있듯이이 여전히 수 없습니다 youtrack.jetbrains.com/issue/KT-11968 : 여기에 관련 SO 스레드가 stackoverflow.com/questions/33911457/...
narko

12

공식 문서는 다음과 같습니다.

코 틀린은 패키지 레벨 함수를위한 정적 메소드를 생성합니다. Kotlin은 또한 @JvmStatic으로 해당 주석을 달면 명명 된 객체 또는 컴패니언 객체에 정의 된 함수에 대한 정적 메소드를 생성 할 수 있습니다. 예를 들면 다음과 같습니다.

코 틀린 정적 메소드

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

이제 foo ()는 Java에서 정적이지만 bar ()는 그렇지 않습니다.

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

8

실제로 30 분 전에이 정확한 질문이 있었으므로 파고 들기 시작했고 이에 대한 해결책이나 해결책을 찾을 수 없었지만 검색하는 동안 Kotlinglang 웹 사이트 에서이 섹션 을 발견 했습니다.

확장은 널 입력 가능 리시버 유형으로 정의 될 수 있습니다. 이러한 확장은 값이 null 인 경우에도 객체 변수에서 호출 될 수 있습니다.

그래서 나는 가장 미친 아이디어를 가지고 있었지만, nullable 수신기로 확장 함수를 정의하고 (실제로 해당 수신기를 사용하지 않고) null 객체에서 호출하지 마십시오! 그래서 나는 그것을 시도했지만 꽤 잘 작동했지만 너무 못 생겼습니다. 다음과 같았습니다 :

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

그래서 수신기 값의 확장 파일에 null 값을 가진 val을 만든 다음 다른 클래스에서 사용하여 문제를 해결했습니다. 예를 들어 NavigationAndroid 에서 클래스에 대한 "정적"확장 기능을 구현 한 방법은 다음과 같습니다. 내 NavigationExtensions.kt 파일에서 :

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

그것을 사용하는 코드에서 :

SNavigation.createNavigateOnClickListener(R.id.action_gameWonFragment_to_gameFragment)

분명히 이것은 클래스 이름이 아니며 null 값을 갖는 클래스 유형의 변수 일뿐입니다. 이것은 변수를 작성해야하기 때문에 확장 메이커 측과 SType실제 클래스 이름 대신 형식 을 사용해야하기 때문에 개발자 측에서는 분명히 추악 하지만 지금 당장 달성 할 수있는 가장 가까운 것입니다 실제 정적 함수와 비교합니다. Kotlin 언어 제작자들은 작성된 문제에 응답하고 해당 기능을 해당 언어로 추가하기를 바랍니다.


2
해키 그러나 영리한 멋진. 이것은 질문에 대한 가장 통찰력있는 답변입니다. Kotlin은 퍼스트 클래스 확장 기능을 위조하는 데 최선을 다하고 있습니다. 따라서 IMO는 최상의 솔루션을 사용하여 정신 건강 문제를 해결합니다. 좋은.
트래비스 Griggs

5

다음과 같은 Companion 객체를 사용하여 정적 메서드를 만들 수 있습니다.

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

그런 다음 다음과 같이 호출 할 수 있습니다.

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

나는 그것이 정적이 아니라고 생각합니다. 컴패니언 객체를 사용하면 클래스 이름을 사용하여 호출 할 수 있지만 여전히 클래스의 인스턴스를 사용합니다. 또한 문제는 정적 함수뿐만 아니라 정적 확장 함수에 관한 것입니다. 그러나 정적 기능을 사용하려면 platformStatic주석을 사용할 수 있습니다 .
Ragunath Jawahar

1
platformStaticJvmStatic현재 Kotlin에서 이름이 변경 되었습니다.
Jayson Minard

0

링크 를 볼 것을 권장합니다 . 보시다시피, 패키지 (파일)의 최상위에 메소드를 선언해야합니다 :

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

이것은

package strings;

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

constans를 사용하면 모든 것이 동일합니다. 이 선언

val UNIX_LINE_SEPARATOR = "\n"

동일하다

public static final String UNIX_LINE_SEPARATOR = "\n";

-3

또한 Kotlin에 정적 ​​확장 메소드를 추가 할 가능성이 매우 좋습니다. 해결 방법으로 지금은 하나의 정적 확장 메소드를 사용하는 대신 여러 클래스에 exntension 메소드를 추가하고 있습니다.

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
원래의 질문은 정적 메소드 로 javaclass를 확장하는 방법에 관한 것이 었습니다 . 귀하의 경우에는 Activity.isDeviceOnline(...)의 인스턴스없이 문자 그대로 호출 할 수 있음을 의미 Activity합니다.
Fabian Zeindl

-4

kotlin에서 확장 메서드를 만들려면 클래스가 아닌 kotlin 파일을 만든 다음 파일에서 메서드를 선언해야합니다. 예 :

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

작업중인 클래스 또는 파일에서 함수를 가져 와서 사용하십시오.


제목은 질문이 아닙니다
daniel.jbatiz
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.