Métodos de extensão estática no Kotlin


142

Como você define um método de extensão estática no Kotlin? Isso é possível? Atualmente, tenho um método de extensão, como mostrado abaixo.

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

A extensão acima pode ser chamada em uma instância.

uberInstance.doMagic(context) // Instance method

mas como faço para torná-lo método estático, como mostrado abaixo.

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

1
O que você quer dizer com "método de extensão estática"?
Andrey Breslav

3
Um método que eu posso chamar sem uma instância, mas ainda é uma extensão da classe. (Métodos estáticos Java regular)
Ragunath Jawahar

Eu acho que pode não ser o uso pretendido da boa pergunta sobre extensões Kotlin , pensei na mesma coisa ao tentar extrapolar o conceito de C #. Mas enquanto na prática o uso é bastante semelhante. As extensões Kotlin, embora afirmem ser enviadas estaticamente, parecem dinamicamente "anexadas", se houver, ou atrasadas, limitadas à instância. Se não estou enganado ... ou talvez eu entendi completamente errado :)
Dan

7
Atualmente, você não pode escrever um método de extensão que seria chamado em um nome de classe em oposição à instância da classe. De fato, a função de extensão aceita um parâmetro receptor que é uma instância, portanto não há como ignorar esta parte. Por outro lado, estamos trabalhando em uma maneira de escrever extensões para objetos de classe, de modo que você chamá-lo em um nome de classe, mas o receptor tem o tipo do objeto de classe
Andrey Breslav

@AndreyBreslav Você pode nos atualizar se as funções de extensão estática serão permitidas? Eu também estou sentindo falta disso.
Lars Blumberg

Respostas:


143

Para conseguir Uber.doMagic(context), você pode gravar uma extensão no objeto complementar de Uber(a declaração do objeto complementar é necessária):

class Uber {
    companion object {}
}

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

79
Isso é legal, mas e se for uma classe Java ou uma classe sem um companheiro?
Jire

31
@Jire nesses casos, não há suporte no Kotlin 1.0
Andrey Breslav

2
Eu pude ver um caso de uso ao estender as classes da estrutura da JVM com valores padrão, por exemplo, String.DEFAULT ou Int.DEFAULT, caso a lógica do seu domínio exija isso (a minha atualmente exige, em conjunto com as classes de dados vazias).
Steffen Funke

36
Funciona apenas enquanto o Uber for da classe Kotlin . Seria bom ter a possibilidade de alargar estaticamente Java aulas bem
kosiara - Bartosz Kosarzycki

6
Isso ainda não é possível, como você pode ver aqui: youtrack.jetbrains.com/issue/KT-11968 Há um thread SO relacionado aqui: stackoverflow.com/questions/33911457/…
narko

12

É o que a documentação oficial diz:

Kotlin gera métodos estáticos para funções em nível de pacote. O Kotlin também pode gerar métodos estáticos para funções definidas em objetos nomeados ou objetos complementares se você anotar essas funções como @JvmStatic. Por exemplo:

Métodos estáticos de Kotlin

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

Agora, foo () é estático em Java, enquanto bar () não é:

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

8

Na verdade, eu tinha essa pergunta exata há 30 minutos, então comecei a procurar e não consegui encontrar nenhuma solução ou solução alternativa para isso, mas durante a pesquisa encontrei esta seção no site da Kotlinglang que afirma que:

Observe que as extensões podem ser definidas com um tipo de receptor anulável. Tais extensões podem ser chamadas em uma variável de objeto, mesmo que seu valor seja nulo.

Então, eu tive a idéia mais louca de todos os tempos: por que não definir uma função de extensão com um receptor nulo (sem realmente usá-lo) e depois chamá-lo em um objeto nulo! Então eu tentei isso, e funcionou muito bem, mas parecia tão feio. Foi assim:

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

Então, eu contornei isso criando um val no meu arquivo de extensões do tipo receptor que tinha um valor nulo e depois o usei na minha outra classe. Então, como exemplo, aqui está como eu implementei uma função de extensão "estática" para a Navigationclasse no Android: No meu arquivo 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) }
}

No código que o usa:

SNavigation.createNavigateOnClickListener(R.id.action_gameWonFragment_to_gameFragment)

Obviamente, este não é um nome de classe, é apenas uma variável do tipo de classe que possui um valor nulo. Isso é obviamente feio no lado do criador de extensões (porque eles precisam criar a variável) e no lado do desenvolvedor (porque eles precisam usar o STypeformato em vez do nome real da classe), mas é o mais próximo que pode ser alcançado agora comparado às funções estáticas reais. Felizmente, os criadores do idioma Kotlin responderão ao problema que foi criado e adicionarão esse recurso ao idioma.


2
Hacky isso pode ser. Mas também legal inteligente. Esta é a resposta mais perspicaz à pergunta. Kotlin está fazendo o melhor possível para falsificar o fornecimento de extensões de primeira classe; portanto, na IMO, você usa as melhores soluções para lidar com suas inanidades. Agradável.
Travis Griggs

5

Você pode criar um método estático usando o objeto Companion, como:

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

e então você pode chamar assim:

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

Eu acredito que isso não é realmente estático. Os objetos complementares permitem que você chame usando o nome da classe, mas ainda usa uma instância da classe. Além disso, a pergunta era sobre funções de extensão estática, não apenas funções estáticas. No entanto, para ter funções estáticas, você pode usar a platformStaticanotação.
Ragunath Jawahar 9/04

1
platformStaticfoi renomeado JvmStaticno Kotlin atual.
Jayson Minard

0

Recomendamos que você veja este link. Como você pode ver lá, você deve declarar o método no nível superior do pacote (arquivo):

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

Isso é igual a

package strings;

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

Com os constantes, tudo é igual. Esta declaração

val UNIX_LINE_SEPARATOR = "\n"

é igual a

public static final String UNIX_LINE_SEPARATOR = "\n";

-3

Também gosto bastante de ter a possibilidade de adicionar métodos de extensão estática no Kotlin. Como solução alternativa, por enquanto, estou adicionando o método exntension a várias classes em vez de usar um método de extensão estática em todas elas.

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
A pergunta original era sobre como uma javaclass pode ser estendida com um método estático . No seu caso, isso significa poder literalmente ligar Activity.isDeviceOnline(...)sem ter uma instância de Activity.
Fabian Zeindl

-4

Para criar um método de extensão no kotlin, você deve criar um arquivo kotlin (não uma classe) e, em seguida, declarar seu método no arquivo Por exemplo:

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

Importe a função na classe ou arquivo em que você está trabalhando e use-a.


O título nem é uma pergunta
daniel.jbatiz
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.