Ten dokument jest zbiorem reguł dotyczących tworzenia publicznych interfejsów API w Javie i Kotlinie w taki sposób, aby kod po użyciu z innego kodu miał wrażenie idiomatycznego język.
Ostatnia aktualizacja: 29.07.2024
Java (na potrzeby konsumpcji Kotlin)
Bez sztywnych słów kluczowych
Nie używaj żadnych twardych słów kluczowych Kotlina jako nazwy metod. lub polach. Wymagają one użycia grawisów w celu zmiany znaczenia przy wywołaniu od Kotlin. Opcjonalne słowa kluczowe, słowa kluczowe modyfikujące oraz specjalne identyfikatory są dozwolone.
Na przykład funkcja when
Mockito wymaga grawisu, gdy jest używana w Kotlin:
val callable = Mockito.mock(Callable::class.java)
Mockito.`when`(callable.call()).thenReturn(/* … */)
Unikaj nazw rozszerzeń Any
Unikaj używania nazw funkcji rozszerzeń w Any
w przypadku
lub nazwy właściwości rozszerzeń w Any
dla
o ile nie jest to absolutnie konieczne. Metody i pola członków zawsze będą
mają pierwszeństwo przed funkcjami lub właściwościami rozszerzeń Any
, może być
trudno było odczytać kod, żeby rozpoznać, który z nich jest wywoływany.
Adnotacje dotyczące wartości null
Każdy niepodstawowy parametr, zwrot i typ pola w publicznym interfejsie API powinien zawierają adnotację dotyczącą wartości null. Typy bez adnotacji są interpretowane jako "platforma" o niejednoznacznej dopuszczalności wartości null.
Domyślnie flagi kompilatora Kotlin promują adnotacje JSR 305, ale je oznaczają z ostrzeżeniami. Możesz też ustawić flagę, by kompilator traktował jako błędów.
Parametry lambda na końcu
Typy parametrów odpowiednie do konwersji SAM powinny być ostatnie.
Na przykład podpis metody Flowable.create()
w RxJava 2 jest zdefiniowany w następujący sposób:
public static <T> Flowable<T> create(
FlowableOnSubscribe<T> source,
BackpressureStrategy mode) { /* … */ }
Ponieważ FlowableOnsubscribe kwalifikuje się do konwersji SAM, wywołania funkcji ta metoda z Kotlina wygląda tak:
Flowable.create({ /* … */ }, BackpressureStrategy.LATEST)
Jeśli jednak parametry zostały odwrócone w podpisie metody, wywołania funkcji można użyć składni lambda na końcu:
Flowable.create(BackpressureStrategy.LATEST) { /* … */ }
Prefiksy usługi
Aby metoda została przedstawiona jako właściwość w Kotlin, ścisły styl typu „fasola” należy użyć przedrostka.
Metody metody dostępu wymagają prefiksu get
, a w przypadku metod zwracających wartość logiczną – is
przedrostek.
public final class User {
public String getName() { /* … */ }
public boolean isActive() { /* … */ }
}
val name = user.name // Invokes user.getName()
val active = user.isActive // Invokes user.isActive()
Powiązane metody mutatorów wymagają prefiksu set
.
public final class User {
public String getName() { /* … */ }
public void setName(String name) { /* … */ }
public boolean isActive() { /* … */ }
public void setActive(boolean active) { /* … */ }
}
user.name = "Bob" // Invokes user.setName(String)
user.isActive = true // Invokes user.setActive(boolean)
Jeśli chcesz udostępniać metody jako właściwości, nie używaj niestandardowych prefiksów, takich jak
has
, set
lub metody dostępu bez prefiksu get
. Metody z niestandardowymi prefiksami
są wciąż wywoływane jako funkcje, które mogą być akceptowalne w zależności
zasady działania tej metody.
Przeciążenie operatora
Zwróć uwagę na nazwy metod, które zezwalają na specjalną składnię wywołania witryny (np. przeciążenie operatorów w Kotlin). Sprawdź, czy metody o nazwach więc warto użyć skróconej składni.
public final class IntBox {
private final int value;
public IntBox(int value) {
this.value = value;
}
public IntBox plus(IntBox other) {
return new IntBox(value + other.value);
}
}
val one = IntBox(1)
val two = IntBox(2)
val three = one + two // Invokes one.plus(two)
Kotlin (do użytkowania Java)
Nazwa pliku
Gdy plik zawiera funkcje lub właściwości najwyższego poziomu, zawsze dodawaj do nich adnotacje
używając @file:JvmName("Foo")
, aby wybrać ładną nazwę.
Domyślnie członkowie najwyższego poziomu w pliku Mojaklasa.kt trafią do klasy o nazwie
MyClassKt
, który jest nieatrakcyjny i wycieka język jako implementacja
szczegóły.
Rozważ dodanie „@file:JvmMultifileClass
”, aby połączyć członków najwyższego poziomu z grupy
wiele plików na jedne zajęcia.
Argumenty lambda
Interfejsy z jedną metodą (SAM) zdefiniowane w Javie można zaimplementować zarówno w Kotlin i Java za pomocą składni lambda, która umieszcza ją w argumencie idiomatycznym. sposób. Kotlin udostępnia kilka opcji definiowania takich interfejsów, z których każda wymaga różnicy.
Preferowana definicja
Funkcje wyższego rzędu przeznaczone do wykorzystania w Javie.
nie powinien przyjmować typów funkcji, które zwracają wartość Unit
, ponieważ
wymaga, aby obiekty wywołujące Java zwracały wartość Unit.INSTANCE
. Zamiast wbudowywać funkcję
wpisz podpis, użyj funkcjonalnych interfejsów (SAM). Poza tym
rozważ użycie funkcjonalnych interfejsów (SAM) zamiast standardowych
podczas definiowania interfejsów, które powinny być używane jako lambda,
który pozwala na idiomatyczne wykorzystanie języka Kotlin.
Spójrzmy na tę definicję Kotlina:
fun interface GreeterCallback {
fun greetName(String name)
}
fun sayHi(greeter: GreeterCallback) = /* … */
Po wywołaniu z Kotlin:
sayHi { println("Hello, $it!") }
W przypadku wywołania z Javy:
sayHi(name -> System.out.println("Hello, " + name + "!"));
Nawet jeśli typ funkcji nie zwraca parametru Unit
, nadal może być on dobry.
pomysłu na utworzenie nazwanego interfejsu, który umożliwi użytkownikom jego wdrożenie przy użyciu
, a nie tylko lambda (zarówno w Kotlin, jak i w Javie).
class MyGreeterCallback : GreeterCallback {
override fun greetName(name: String) {
println("Hello, $name!");
}
}
Unikaj typów funkcji, które zwracają wartość Unit
Spójrzmy na tę definicję Kotlina:
fun sayHi(greeter: (String) -> Unit) = /* … */
Wymaga zwracania przez elementy wywołujące Javy Unit.INSTANCE
:
sayHi(name -> {
System.out.println("Hello, " + name + "!");
return Unit.INSTANCE;
});
Unikaj interfejsów funkcjonalnych, gdy implementacja ma mieć stan
Kiedy implementacja interfejsu ma mieć określony stan, za pomocą funkcji lambda
która nie ma sensu. Porównywalny to ważny przykład:
ponieważ ma porównać wartości this
z other
, a lambda nie zawierają parametru this
. Nie
wprowadzenie przedrostka fun
w interfejsie wymusza użycie elementu wywołującego object : ...
składni, która pozwala określić stan i zapewnia wskazówkę dla wywołującego.
Spójrzmy na tę definicję Kotlina:
// No "fun" prefix.
interface Counter {
fun increment()
}
Zapobiega składni lambda w Kotlin, wymagając tej dłuższej wersji:
runCounter(object : Counter {
private var increments = 0 // State
override fun increment() {
increments++
}
})
Unikaj reklam ogólnych (Nothing
)
Typ, którego ogólny parametr to Nothing
, jest udostępniany w Javie w postaci nieprzetworzonej. Nierafinowane
są rzadko używane w Javie i należy ich unikać.
Wyjątki dla dokumentów
Funkcje, które mogą zgłaszać zaznaczone wyjątki, powinny dokumentować je za pomocą
@Throws
Wyjątki środowiska wykonawczego powinny być udokumentowane w KDoc.
Zwracaj uwagę na interfejsy API, do których funkcja przekazuje dostęp, ponieważ mogą one zgłaszać zaznaczone wyjątki, na które Kotlin może jednak dyskretnie zezwolić na ich rozpowszechnianie.
Kopie w obronie
Gdy zwracasz udostępnione lub nienależące do Ciebie kolekcje z publicznych interfejsów API, zapakuj je w pojemnikach, których nie można zmienić, ani wykonać kopii obronnej. Pomimo Kotlina nie egzekwuje on właściwości tylko do odczytu, nie jest więc egzekwowane w tym języku z boku strony. Bez otoki lub tekstu defensywnego niezmienniki mogą zostać naruszone przez które zwraca długoterminowe odwołanie do kolekcji.
Funkcje towarzyszące
Funkcje publiczne w obiekcie towarzyszącym muszą mieć adnotację @JvmStatic
nie są dostępne jako metoda statyczna.
Bez adnotacji te funkcje są dostępne tylko jako metody instancji
w statycznym polu Companion
.
Niepoprawnie: brak adnotacji
class KotlinClass {
companion object {
fun doWork() {
/* … */
}
}
}
public final class JavaClass {
public static void main(String... args) {
KotlinClass.Companion.doWork();
}
}
Prawidłowo: adnotacja @JvmStatic
class KotlinClass {
companion object {
@JvmStatic fun doWork() {
/* … */
}
}
}
public final class JavaClass {
public static void main(String... args) {
KotlinClass.doWork();
}
}
Stałe towarzyszące
Publiczne właściwości inne niż const
, które są stałymi efektywnymi w companion
object
, muszą mieć adnotację @JvmField
, aby były widoczne jako pole statyczne.
Bez adnotacji te właściwości są dostępne tylko z dziwnymi nazwami
instancja „getters” w statycznym polu Companion
. Zamiast tego korzystam z @JvmStatic
funkcji @JvmField
przenosi elementy „getters” o dziwnych nazwach do metod statycznych na zajęciach,
co nadal jest nieprawidłowe.
Niepoprawnie: brak adnotacji
class KotlinClass {
companion object {
const val INTEGER_ONE = 1
val BIG_INTEGER_ONE = BigInteger.ONE
}
}
public final class JavaClass {
public static void main(String... args) {
System.out.println(KotlinClass.INTEGER_ONE);
System.out.println(KotlinClass.Companion.getBIG_INTEGER_ONE());
}
}
Nieprawidłowo: adnotacja @JvmStatic
class KotlinClass {
companion object {
const val INTEGER_ONE = 1
@JvmStatic val BIG_INTEGER_ONE = BigInteger.ONE
}
}
public final class JavaClass {
public static void main(String... args) {
System.out.println(KotlinClass.INTEGER_ONE);
System.out.println(KotlinClass.getBIG_INTEGER_ONE());
}
}
Prawidłowo: adnotacja @JvmField
class KotlinClass {
companion object {
const val INTEGER_ONE = 1
@JvmField val BIG_INTEGER_ONE = BigInteger.ONE
}
}
public final class JavaClass {
public static void main(String... args) {
System.out.println(KotlinClass.INTEGER_ONE);
System.out.println(KotlinClass.BIG_INTEGER_ONE);
}
}
Nazewnictwo idiomatyczne
W Kotlin obowiązują inne konwencje wywoływania niż w Javie, co może zmienić
funkcji nazw. Użyj @JvmName
, aby zaprojektować nazwy tak, aby wyglądały idiomatyczne
dla konwencji obu języków lub aby dopasować je do odpowiednich bibliotek standardowych.
nazwy.
Najczęściej dzieje się tak w przypadku funkcji i właściwości rozszerzeń bo lokalizacja odbiornika jest inna.
sealed class Optional<T : Any>
data class Some<T : Any>(val value: T): Optional<T>()
object None : Optional<Nothing>()
@JvmName("ofNullable")
fun <T> T?.asOptional() = if (this == null) None else Some(this)
// FROM KOTLIN:
fun main(vararg args: String) {
val nullableString: String? = "foo"
val optionalString = nullableString.asOptional()
}
// FROM JAVA:
public static void main(String... args) {
String nullableString = "Foo";
Optional<String> optionalString =
Optionals.ofNullable(nullableString);
}
Przeciążenia funkcji na potrzeby ustawień domyślnych
Funkcje z parametrami, które mają wartość domyślną, muszą używać parametru @JvmOverloads
.
Bez tej adnotacji nie można wywołać funkcji za pomocą
wartości domyślnych.
Jeśli korzystasz z metody @JvmOverloads
, sprawdź wygenerowane metody, aby mieć pewność, że każda
mają sens. Jeśli nie, wykonaj co najmniej jedną z tych refaktoryzacji
aż do satysfakcji:
- Zmień kolejność parametrów, tak aby preferować wartości domyślne na ich końcu.
- Przenieś wartości domyślne do ręcznych przeciążeń funkcji.
Niepoprawnie: nie @JvmOverloads
class Greeting {
fun sayHello(prefix: String = "Mr.", name: String) {
println("Hello, $prefix $name")
}
}
public class JavaClass {
public static void main(String... args) {
Greeting greeting = new Greeting();
greeting.sayHello("Mr.", "Bob");
}
}
Poprawnie: adnotacja @JvmOverloads
.
class Greeting {
@JvmOverloads
fun sayHello(prefix: String = "Mr.", name: String) {
println("Hello, $prefix $name")
}
}
public class JavaClass {
public static void main(String... args) {
Greeting greeting = new Greeting();
greeting.sayHello("Bob");
}
}
Kontrola lin
Wymagania
- Wersja Android Studio: 3.2 Canary 10 lub nowsza
- Wersja wtyczki Androida do obsługi Gradle: 3.2 lub nowsza
Obsługiwane testy
Dostępne są teraz testy licencji Androida, które pomagają w wykrywaniu i zgłaszaniu niektórych opisane wcześniej problemy ze interoperacyjnością. Tylko problemy w Javie (dla Kotlin) konsumpcji treści). Obsługiwane testy są następujące:
- Nieznana wartość null
- Dostęp do usługi
- Brak słów kluczowych Hard Kotlin
- Ostatnie parametry lambda
Android Studio,
Aby włączyć tę funkcję, kliknij Plik > Ustawienia > Edytor > inspekcje, Zaznacz reguły, które chcesz włączyć w sekcji Interoperacyjność usługi Kotlin:
Rysunek 1. Ustawienia interoperacyjności z Kotlin w Android Studio.
Gdy zaznaczysz reguły, które chcesz włączyć, nowe weryfikacje będą uruchamianych podczas inspekcji kodu (Analiza > Zbadaj kod...).
Kompilacje z użyciem wiersza poleceń
Aby włączyć te kontrole w kompilacji wiersza poleceń, dodaj następujący wiersz w argumencie
Twój plik build.gradle
:
Odlotowe
android { ... lintOptions { enable 'Interoperability' } }
Kotlin
android { ... lintOptions { enable("Interoperability") } }
Pełny zestaw konfiguracji obsługiwanych w lintOptions znajdziesz w Dokumentacja DSL Gradle dotycząca Androida.
Następnie uruchom ./gradlew lint
z poziomu wiersza poleceń.