Java & Kotlin Flashcards
Pytanie: Jakie są główne różnice między Javą a Kotlinem?
Odpowiedź: Kotlin jest bardziej ekspresyjny, posiada wbudowane funkcje takie jak rozszerzenia, korutyny, typy niemutowalne, itp. Kotlin eliminuje wiele błędów w czasie kompilacji, takich jak nullowe wskaźniki. Java jest starszym językiem i ma więcej kodu bazowego, ale Kotlin jest w pełni interoperacyjny z Javą.
Pytanie: Co to są rozszerzenia w Kotlinie?
Odpowiedź: Rozszerzenia w Kotlinie pozwalają dodawać nowe funkcje do istniejących klas bez modyfikowania ich kodu źródłowego.
Pytanie: Jak w Javie można zaimplementować singleton?
Odpowiedź: W Javie singleton można zaimplementować za pomocą prywatnego konstruktora i statycznej zmiennej instancji. Można również użyć wewnętrznej klasy statycznej lub wyliczenia (enum) do tego celu.
Pytanie: Co to są korutyny w Kotlinie i do czego służą?
Korutyny to lekkie wątki, które pozwalają na asynchroniczne programowanie w Kotlinie w sposób bardziej czytelny i wydajny. Dzięki korutynom można pisać asynchroniczny kod w sposób sekwencyjny, co czyni go bardziej zrozumiałym. Korutyny są szczególnie przydatne w operacjach I/O, które mogą trwać długo, takich jak operacje sieciowe, dostęp do bazy danych czy odczyt/zapis plików.
import kotlinx.coroutines.*
fun main() = runBlocking {
val result = async {
computeSomething()
}
println(“Wynik: ${result.await()}”)
}
suspend fun computeSomething(): Int {
delay(1000) // symuluje długotrwałą operację
return 42
}
runBlocking, który blokuje główny wątek do momentu zakończenia wszystkich korutyn.
Pytanie: Jakie są główne różnice między listami w Javie a listami w Kotlinie?
Odpowiedź: W Kotlinie listy są domyślnie niemutowalne (List), podczas gdy w Javie są one mutowalne (ArrayList). Aby uzyskać mutowalną listę w Kotlinie, można użyć MutableList.
Pytanie: Jak w Kotlinie można zabezpieczyć się przed nullowymi wskaźnikami?
Odpowiedź: Kotlin wprowadza system typów, który rozróżnia referencje, które mogą przyjmować wartość null (String?) od tych, które nie mogą (String). Aby operować na wartościach, które mogą być null, można użyć operatora ?., !! lub instrukcji let.
Pytanie: Jakie są główne zalety używania Kotlinu zamiast Javy w projektach Android?
Odpowiedź: Kotlin oferuje krótszy, bardziej czytelny kod, wbudowane funkcje do obsługi współczesnych funkcji programowania, lepsze zarządzanie nullami, korutyny do asynchronicznego programowania oraz pełną interoperacyjność z Javą.
Pytanie: Jakie są różnice między var a val w Kotlinie?
Odpowiedź: val oznacza niemutowalną referencję (odpowiednik final w Javie), podczas gdy var oznacza mutowalną referencję.
Pytanie: Jakie są główne cechy paradygmatu programowania funkcyjnego w Kotlinie?
Odpowiedź: Kotlin obsługuje wyższego rzędu funkcje, niemutowalność, operacje na kolekcjach takie jak map, filter, reduce, oraz wyrażenia lambda.
Paradygmat programowania funkcyjnego w Kotlinie charakteryzuje się kilkoma kluczowymi cechami:
-
Niemutowalność: W programowaniu funkcyjnym zaleca się, aby dane były niemutowalne. Oznacza to, że raz utworzone, dane nie są modyfikowane. W Kotlinie można używać słowa kluczowego
val
do deklarowania niemutowalnych zmiennych. - Funkcje wyższego rzędu: Kotlin obsługuje funkcje, które przyjmują inne funkcje jako argumenty lub zwracają je jako wynik. Pozwala to na tworzenie bardziej ogólnych i wielokrotnie używanych funkcji.
- Wyrażenia lambda: Kotlin obsługuje wyrażenia lambda, które są anonimowymi funkcjami, mogą być przekazywane jako argumenty do funkcji wyższego rzędu.
-
Operacje na kolekcjach: Kotlin oferuje bogaty zestaw funkcji do przetwarzania kolekcji w stylu funkcyjnym, takich jak
map
,filter
,reduce
,flatMap
itp. -
Leniwość: Kotlin obsługuje sekwencje (
Sequence
), które są leniwymi kolekcjami. Operacje na sekwencjach są wykonywane leniwie, co oznacza, że są wykonywane tylko wtedy, gdy są naprawdę potrzebne. - Rozszerzenia funkcji: Kotlin pozwala na dodawanie nowych funkcji do istniejących typów bez modyfikowania ich kodu źródłowego za pomocą funkcji rozszerzeń.
- Brak efektów ubocznych: W programowaniu funkcyjnym zaleca się pisanie funkcji bez efektów ubocznych, co oznacza, że funkcja zwraca wartość wyłącznie na podstawie swoich argumentów i nie wpływa na żadne zewnętrzne stany.
-
Typy algebraiczne: Chociaż Kotlin nie obsługuje pełnych typów algebraicznych, jak niektóre języki funkcyjne, oferuje mechanizmy takie jak
sealed class
, które pozwalają na modelowanie ograniczonych hierarchii typów w sposób zbliżony do typów algebraicznych. - Funkcje lokalne i zagnieżdżone: W Kotlinie można definiować funkcje wewnątrz innych funkcji, co pozwala na tworzenie bardziej modularnego i zorganizowanego kodu.
- Deklaratywność: W programowaniu funkcyjnym skupiamy się na tym, “co” chcemy osiągnąć, a nie “jak” to zrobić. Kotlin promuje ten styl pisania kodu, zwłaszcza podczas pracy z kolekcjami.
Podsumowując, Kotlin integruje wiele cech programowania funkcyjnego, co czyni go wyjątkowo elastycznym językiem, który pozwala programistom korzystać zarówno z paradygmatu obiektowego, jak i funkcyjnego.
Pytanie: Jakie są różnice między ArrayList w Javie a ArrayList w Kotlinie?
Odpowiedź: W Kotlinie ArrayList jest jedynie aliasem dla klasy java.util.ArrayList. Jednak Kotlin oferuje dodatkowe metody rozszerzeń dla kolekcji, które nie są dostępne w standardowej Javie.
Pytanie: Jakie są różnice między == w Javie a == w Kotlinie?
Odpowiedź: W Javie == porównuje referencje, podczas gdy w Kotlinie == porównuje zawartość (odpowiednik equals() w Javie). Aby porównać referencje w Kotlinie, używa się ===.
Pytanie: Jak w Kotlinie można zdefiniować funkcję rozszerzenia dla klasy String?
fun String.myExtension(): Type {
// ciało funkcji
}
Pytanie: Jakie są różnice między interfejsami w Javie 8 a interfejsami w Kotlinie?
Odpowiedź: Zarówno Java 8, jak i Kotlin pozwalają na definiowanie metod domyślnych w interfejsach. Jednak Kotlin pozwala również na definiowanie właściwości w interfejsach.
Pytanie: Co to jest “sealed class” w Kotlinie i do czego służy?
W Kotlinie sealed class (klasa zapieczętowana) to specjalny rodzaj klasy, który ma ograniczony zestaw klas podrzędnych. Innymi słowy, klasy podrzędne klasy zapieczętowanej muszą być zdefiniowane w tym samym pliku co klasa zapieczętowana. Dzięki temu możemy mieć pewność, że zestaw klas podrzędnych jest znany i ograniczony, co jest szczególnie przydatne w przypadku modelowania ograniczonych hierarchii.
Klasy zapieczętowane są często używane w połączeniu z instrukcjami when, aby zapewnić, że wszystkie możliwe przypadki są obsługiwane.
sealed class Operation {
class Add(val value: Int) : Operation()
class Subtract(val value: Int) : Operation()
class Multiply(val value: Int) : Operation()
class Divide(val value: Int) : Operation()
}
fun execute(x: Int, op: Operation): Int = when (op) {
is Operation.Add -> x + op.value
is Operation.Subtract -> x - op.value
is Operation.Multiply -> x * op.value
is Operation.Divide -> x / op.value
}
W powyższym przykładzie mamy klasę zapieczętowaną Operation z czterema klasami podrzędnymi reprezentującymi różne operacje matematyczne. Funkcja execute przyjmuje wartość x i operację, a następnie wykonuje odpowiednią operację matematyczną w zależności od typu operacji.
Główną zaletą użycia sealed class w tym kontekście jest to, że jeśli dodamy nową klasę podrzędną do Operation, kompilator wymusi na nas obsługę tego nowego przypadku w instrukcji when, co pomaga w zapewnieniu kompletności i bezpieczeństwa kodu.
Pytanie: Jakie są różnice między strumieniami (Stream) w Javie a sekwencjami (Sequence) w Kotlinie?
Odpowiedź: Zarówno strumienie, jak i sekwencje służą do operacji na danych w sposób leniwy. Główna różnica polega na tym, że operacje na strumieniach w Javie są zawsze leniwe, podczas gdy w Kotlinie kolekcje są domyślnie zachłanne, a sekwencje są leniwe.
Strumienie (Stream) w Javie i sekwencje (Sequence) w Kotlinie służą do reprezentowania leniwych kolekcji danych i umożliwiają przetwarzanie elementów w sposób deklaratywny. Chociaż obie te abstrakcje mają wiele podobieństw, istnieją pewne kluczowe różnice:
Pochodzenie i interoperacyjność: Strumienie są częścią standardowej biblioteki Javy od Javy 8. Sekwencje są specyficzne dla Kotlin i są częścią standardowej biblioteki Kotlin. Leniwość: Zarówno strumienie, jak i sekwencje są leniwe w swoim działaniu. Oznacza to, że operacje są wykonywane tylko wtedy, gdy jest to konieczne (np. podczas terminalnej operacji, takiej jak toList dla sekwencji lub collect dla strumieni). Terminologia: W Javie operacje na strumieniach są podzielone na operacje pośrednie (np. map, filter) i terminalne (np. collect, forEach). W Kotlinie terminologia jest podobna, ale niektóre nazwy funkcji mogą się różnić. Na przykład, zamiast collect, w Kotlinie używa się toList lub innych funkcji konwersji. Efekty uboczne: W przypadku strumieni w Javie zaleca się unikanie efektów ubocznych podczas przetwarzania, ponieważ mogą one prowadzić do nieprzewidywalnych wyników, zwłaszcza w połączeniu z równoległym przetwarzaniem strumieni. Sekwencje w Kotlinie również zalecają unikanie efektów ubocznych, ale Kotlin oferuje funkcję onEach do dodawania efektów ubocznych w sposób kontrolowany. Równoległe przetwarzanie: Strumienie w Javie oferują wsparcie dla równoległego przetwarzania za pomocą metody parallelStream(). Sekwencje w Kotlinie są zawsze sekwencyjne i nie oferują natywnego wsparcia dla równoległego przetwarzania. Jeśli potrzebujesz równoległego przetwarzania w Kotlinie, musisz skorzystać z innych mechanizmów, takich jak korutyny. Tworzenie: Strumienie można tworzyć za pomocą różnych metod, takich jak Stream.of(), Collection.stream(), IntStream.range() itp. Sekwencje w Kotlinie można tworzyć za pomocą funkcji sequenceOf(), asSequence(), generateSequence() itp.