Java & Kotlin Flashcards

1
Q

Pytanie: Jakie są główne różnice między Javą a Kotlinem?

A

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ą.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
2
Q

Pytanie: Co to są rozszerzenia w Kotlinie?

A

Odpowiedź: Rozszerzenia w Kotlinie pozwalają dodawać nowe funkcje do istniejących klas bez modyfikowania ich kodu źródłowego.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
3
Q

Pytanie: Jak w Javie można zaimplementować singleton?

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
4
Q

Pytanie: Co to są korutyny w Kotlinie i do czego służą?

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
5
Q

Pytanie: Jakie są główne różnice między listami w Javie a listami w Kotlinie?

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
6
Q

Pytanie: Jak w Kotlinie można zabezpieczyć się przed nullowymi wskaźnikami?

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
7
Q

Pytanie: Jakie są główne zalety używania Kotlinu zamiast Javy w projektach Android?

A

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ą.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
8
Q

Pytanie: Jakie są różnice między var a val w Kotlinie?

A

Odpowiedź: val oznacza niemutowalną referencję (odpowiednik final w Javie), podczas gdy var oznacza mutowalną referencję.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
9
Q

Pytanie: Jakie są główne cechy paradygmatu programowania funkcyjnego w Kotlinie?

A

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:

  1. 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.
  2. 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.
  3. 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.
  4. Operacje na kolekcjach: Kotlin oferuje bogaty zestaw funkcji do przetwarzania kolekcji w stylu funkcyjnym, takich jak map, filter, reduce, flatMap itp.
  5. 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.
  6. Rozszerzenia funkcji: Kotlin pozwala na dodawanie nowych funkcji do istniejących typów bez modyfikowania ich kodu źródłowego za pomocą funkcji rozszerzeń.
  7. 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.
  8. 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.
  9. 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.
  10. 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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
10
Q

Pytanie: Jakie są różnice między ArrayList w Javie a ArrayList w Kotlinie?

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
11
Q

Pytanie: Jakie są różnice między == w Javie a == w Kotlinie?

A

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ę ===.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
12
Q

Pytanie: Jak w Kotlinie można zdefiniować funkcję rozszerzenia dla klasy String?

A

fun String.myExtension(): Type {
// ciało funkcji
}

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
13
Q

Pytanie: Jakie są różnice między interfejsami w Javie 8 a interfejsami w Kotlinie?

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
14
Q

Pytanie: Co to jest “sealed class” w Kotlinie i do czego służy?

A

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.

How well did you know this?
1
Not at all
2
3
4
5
Perfectly
15
Q

Pytanie: Jakie są różnice między strumieniami (Stream) w Javie a sekwencjami (Sequence) w Kotlinie?

A

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.
How well did you know this?
1
Not at all
2
3
4
5
Perfectly
16
Q

Pytanie: Jak w Kotlinie można zabezpieczyć klasę przed dziedziczeniem?

A

Odpowiedź: W Kotlinie klasy są domyślnie ostateczne (final). Aby klasa mogła być rozszerzana, musi być oznaczona słowem kluczowym open.

17
Q

Pytanie: Jakie są główne różnice między wyjątkami kontrolowanymi a niekontrolowanymi w Javie?

A

Odpowiedź: Wyjątki kontrolowane muszą być deklarowane w sygnaturze metody lub obsługiwane w bloku try-catch, podczas gdy wyjątki niekontrolowane nie mają takiego wymogu. W Kotlinie wszystkie wyjątki są niekontrolowane.

18
Q

Pytanie: Jakie są główne różnice między lateinit a lazy w Kotlinie?

A

Odpowiedź: lateinit jest używany, gdy chcemy zadeklarować niemutowalną zmienną bez inicjalizacji, a następnie zainicjować ją później. lazy jest używane do leniwej inicjalizacji, gdzie wartość jest inicjowana tylko wtedy, gdy jest po raz pierwszy używana.

19
Q

Pytanie: Jakie są różnice między abstrakcyjnymi klasami a interfejsami w Javie?

A

Odpowiedź: Abstrakcyjne klasy mogą mieć stan (zmienne członkowskie), podczas gdy interfejsy nie mogą. Od Javy 8, interfejsy mogą mieć metody domyślne i statyczne. Klasy w Javie mogą dziedziczyć tylko jedną klasę, ale mogą implementować wiele interfejsów.

20
Q

Pytanie: Co to jest “data class” w Kotlinie i jakie są jego główne cechy?

A

Odpowiedź: data class to specjalny rodzaj klasy w Kotlinie, który jest głównie używany do przechowywania danych. Kotlin automatycznie generuje dla niej metody takie jak equals(), hashCode() i toString().

21
Q

Pytanie: Jakie są różnice między List a Set w Javie?

A

Odpowiedź: List przechowuje elementy w kolejności ich wstawienia i dopuszcza duplikaty, podczas gdy Set przechowuje unikalne elementy bez określonej kolejności.

22
Q

Pytanie: Jak w Kotlinie można zaimplementować wzorzec projektowy Singleton?

A

Odpowiedź: W Kotlinie można użyć obiektu (object) do zaimplementowania wzorca Singleton. Obiekt jest tworzony w momencie pierwszego dostępu.

23
Q

Pytanie: Jakie są różnice między tradycyjnymi pętlami for w Javie a pętlami for w Kotlinie?

A

Odpowiedź: W Kotlinie tradycyjne pętle for są używane głównie do iteracji przez zakresy lub kolekcje za pomocą słowa kluczowego in. W Javie pętle for mogą być bardziej elastyczne, ale Kotlin oferuje bardziej ekspresyjne konstrukcje, takie jak forEach.

24
Q

Pytanie: Jakie są główne różnice między throw w Javie a throw w Kotlinie?

A

Odpowiedź: W obu językach throw służy do rzucania wyjątków. Jednak w Kotlinie throw jest wyrażeniem, co oznacza, że może być używane w miejscach, gdzie oczekiwana jest wartość, np. w wyrażeniach lambda.

25
Q

Pytanie: Jakie są różnice między SAM (Single Abstract Method) w Javie a SAM w Kotlinie?

A

W Javie i Kotlinie pojęcie SAM (Single Abstract Method) odnosi się do interfejsów, które deklarują tylko jedną metodę abstrakcyjną. Są one używane w kontekście wyrażeń lambda i anonimowych klas wewnętrznych, które pozwalają na bardziej zwięzły sposób implementacji tych interfejsów.

Oto główne różnice między SAM w Javie a SAM w Kotlinie:

  1. Wyrażenia lambda:
    • W Javie wyrażenia lambda zostały wprowadzone w Java 8 i mogą być używane do implementacji interfejsów SAM.
    • W Kotlinie wyrażenia lambda są bardziej zaawansowane i mogą być używane nie tylko do implementacji interfejsów SAM, ale także do bezpośredniego przekazywania funkcji jako parametrów.
  2. Konwersja:
    • W Kotlinie istnieje automatyczna konwersja wyrażeń lambda na interfejsy SAM, co oznacza, że możesz przekazać wyrażenie lambda tam, gdzie oczekiwany jest interfejs SAM.
    • W Javie musisz jawnie użyć wyrażenia lambda lub anonimowej klasy wewnętrznej do implementacji interfejsu SAM.
  3. Funkcyjne interfejsy:
    • Kotlin wprowadza pojęcie “funkcyjnych interfejsów” (ang. functional interface), które są zgodne z interfejsami SAM w Javie, ale mogą być również używane z funkcjami w Kotlinie.
  4. Interfejsy z metodami domyślnymi:
    • W Javie interfejsy SAM mogą zawierać metody domyślne, które nie przeszkadzają w wykorzystaniu wyrażeń lambda.
    • W Kotlinie, aby interfejs był traktowany jako SAM, nie może on zawierać żadnych innych metod abstrakcyjnych oprócz jednej.

Przykładowy kod w Kotlinie:

```kotlin
fun interface ClickListener {
fun onClick(event: String)
}

fun setClickListener(listener: ClickListener) {
listener.onClick(“Clicked”)
}

fun main() {
// Użycie wyrażenia lambda w Kotlinie dla interfejsu SAM
setClickListener { event -> println(“Event: $event”) }
}
~~~

Przykładowy kod w Javie:

```java
@FunctionalInterface
interface ClickListener {
void onClick(String event);
}

public class Main {
public static void setClickListener(ClickListener listener) {
listener.onClick(“Clicked”);
}

public static void main(String[] args) {
    // Użycie wyrażenia lambda w Javie dla interfejsu SAM
    setClickListener(event -> System.out.println("Event: " + event));
} } ~~~

W Kotlinie, dzięki funkcjonalnym interfejsom, możemy używać wyrażeń lambda w bardzo zwięzły sposób, co jest jedną z zalet tego języka w porównaniu do Javy.

26
Q

Pytanie: Jakie są różnice między konstruktorem pierwotnym a wtórnym w Kotlinie?

A

Odpowiedź: Konstruktor pierwotny jest częścią deklaracji klasy i jest używany głównie do inicjalizacji właściwości. Konstruktory wtórne są zdefiniowane w ciele klasy i muszą delegować do konstruktora pierwotnego za pomocą słowa kluczowego this.

W Kotlinie istnieją dwa typy konstruktorów: pierwotny (primary constructor) i wtórny (secondary constructor). Oto ich główne różnice:

  1. Definicja:
    • Konstruktor pierwotny: Jest zintegrowany z deklaracją klasy i pojawia się bezpośrednio po nazwie klasy. Może mieć parametry, ale nie ma ciała.
    • Konstruktor wtórny: Jest zdefiniowany w ciele klasy za pomocą słowa kluczowego constructor. Może mieć zarówno parametry, jak i ciało.
  2. Inicjalizacja:
    • Konstruktor pierwotny: Inicjalizuje klasy główne i może być używany do inicjalizacji właściwości klasy bezpośrednio. Bloki inicjalizacji (init) są używane w połączeniu z konstruktorem pierwotnym.
    • Konstruktor wtórny: Musi wywołać konstruktor pierwotny lub inny konstruktor wtórny za pomocą słowa kluczowego this.
  3. Użycie:
    • Konstruktor pierwotny: Jest zazwyczaj używany do podstawowej inicjalizacji klasy.
    • Konstruktor wtórny: Jest używany, gdy klasa wymaga dodatkowej logiki inicjalizacji lub różnych sposobów inicjalizacji.
  4. Przykłady:
    • Konstruktor pierwotny:
      kotlin
      class Person(val name: String, val age: Int) {
          init {
              println("Osoba o imieniu $name i wieku $age lat została utworzona.")
          }
      }
    • Konstruktor wtórny:
      ```kotlin
      class Rectangle {
      val area: Double
      constructor(side: Double) {
          area = side * side
      }
      
      constructor(length: Double, width: Double) {
          area = length * width
      } } ```
  5. Ograniczenia:
    • Konstruktor pierwotny: Nie może zawierać żadnej logiki inicjalizacji poza inicjalizacją właściwości i blokami init.
    • Konstruktor wtórny: Musi zawsze odwoływać się do konstruktora pierwotnego, bezpośrednio lub pośrednio.

Podsumowując, konstruktor pierwotny w Kotlinie jest bardziej zwięzły i jest używany do prostych inicjalizacji, podczas gdy konstruktory wtórne są używane do bardziej złożonych scenariuszy inicjalizacji. Jednak w większości przypadków konstruktor pierwotny jest wystarczający.

27
Q

Pytanie: Jakie są różnice między apply, run, let, also i with w Kotlinie?

A

W Kotlinie apply, run, let, also, i with są funkcjami rozszerzającymi, które służą do wykonywania bloków kodu z kontekstem obiektu. Oto ich różnice:

  1. apply:
    • Zwraca kontekst obiektu.
    • Wewnątrz bloku apply, this odnosi się do obiektu.
    • Używane głównie do inicjalizacji obiektów.
    kotlin
    val person = Person().apply {
        name = "John"
        age = 30
    }
  2. run:
    • Zwraca wynik bloku.
    • Wewnątrz bloku run, this odnosi się do obiektu.
    • Używane, gdy potrzebujemy zarówno inicjalizacji, jak i zwrócenia wyniku.
    kotlin
    val greeting = person.run {
        "The name is $name and the age is $age."
    }
  3. let:
    • Zwraca wynik bloku.
    • Wewnątrz bloku let, it odnosi się do obiektu.
    • Używane do operacji, które mogą zwracać wynik, często w połączeniu z wywołaniami łańcuchowymi.
    kotlin
    val numbers = mutableListOf("One", "Two", "Three")
    val count = numbers.map { it.length }.let {
        it.sum()
    }
  4. also:
    • Zwraca kontekst obiektu.
    • Wewnątrz bloku also, it odnosi się do obiektu.
    • Używane do dodatkowych operacji, które nie zmieniają obiektu.
    kotlin
    numbers.also {
        println("The list elements are: $it")
    }.add("Four")
  5. with:
    • Zwraca wynik bloku.
    • Wewnątrz bloku with, this odnosi się do obiektu.
    • Nie jest funkcją rozszerzającą, a zwykłą funkcją. Używana, gdy mamy obiekt i chcemy wykonać na nim wiele operacji.
    kotlin
    with(person) {
        println(name)
        println(age)
    }

Każda z tych funkcji jest używana w zależności od tego, co chcemy osiągnąć: czy chcemy zwrócić kontekst obiektu, wynik bloku, czy też mamy inny cel, jak wykonanie dodatkowych operacji niezwiązanych bezpośrednio z obiektem.

28
Q

Pytanie: Jakie są różnice między public, private, protected i internal w Kotlinie?

A

Odpowiedź: To są modyfikatory dostępu w Kotlinie:

public: Dostępny wszędzie.
private: Dostępny tylko w obrębie klasy lub pliku źródłowego.
protected: Dostępny w klasie i jej klasach pochodnych.
internal: Dostępny w obrębie tego samego modułu.
28
Q

Pytanie: Jakie są różnice między annotation w Javie a annotation w Kotlinie?

A

Adnotacje w Javie i Kotlinie służą podobnym celom - są to metadane, które można dołączyć do kodu i które mogą być następnie używane w czasie kompilacji lub w czasie działania programu. Oto kilka kluczowych różnic:

  1. Składnia:
    • W Kotlinie adnotacje są poprzedzone znakiem @, podobnie jak w Javie, ale Kotlin pozwala na używanie adnotacji z parametrami bez nawiasów, jeśli jest tylko jeden parametr i jego nazwa to value.
    • Kotlin pozwala również na używanie adnotacji na wyrażeniach, co nie jest możliwe w Javie.
  2. Zastosowanie:
    • Kotlin rozszerza możliwości zastosowania adnotacji na typy, co oznacza, że można adnotować nie tylko deklaracje, ale również typy wyrażeń. W Javie adnotacje na typach zostały dodane w Java 8, ale ich zastosowanie jest bardziej ograniczone.
  3. Parametry domyślne:
    • Adnotacje w Kotlinie mogą mieć parametry z wartościami domyślnymi, co nie jest możliwe w Javie.
  4. Adnotacje meta:
    • Kotlin wprowadza koncepcję adnotacji meta, które pozwalają na adnotowanie innych adnotacji. W Javie można osiągnąć podobny efekt za pomocą adnotacji złożonych.
  5. Adnotacje używania:
    • Kotlin pozwala na określenie, gdzie adnotacja może być użyta, za pomocą klasy @Target, podobnie jak w Javie, ale Kotlin ma różne zestawy AnnotationTarget.
  6. Dziedziczenie adnotacji:
    • W Kotlinie adnotacje nie są dziedziczone domyślnie, podczas gdy w Javie można użyć @Inherited do dziedziczenia adnotacji na klasy pochodne.
  7. Adnotacje powtórzone:
    • Kotlin obsługuje adnotacje powtórzone natywnie, podczas gdy w Javie wymaga to użycia kontenera adnotacji do Java 7; od Java 8, Java również obsługuje adnotacje powtórzone bezpośrednio.

Przykład adnotacji w Kotlinie:

```kotlin

annotation class MyAnnotation(val value: String = “default”)

@MyAnnotation(“example”)
class MyClass {
@field:MyAnnotation // Adnotacja stosowana do pola klasy
var myField = 0

@get:MyAnnotation // Adnotacja stosowana do gettera
val myProperty: String
    get() = "Hello" } ~~~

Przykład adnotacji w Javie:

```java
import java.lang.annotation.*;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
public @interface MyAnnotation {
String value() default “default”;
}

@MyAnnotation(“example”)
public class MyClass {
@MyAnnotation // Adnotacja stosowana do pola
private int myField;

@MyAnnotation // Adnotacja stosowana do metody
public String getMyProperty() {
    return "Hello";
} } ~~~

W Kotlinie adnotacje są bardziej elastyczne i mogą być stosowane w szerszym zakresie miejsc niż w Javie, co daje programistom większą moc wyrażania intencji w kodzie.

29
Q

Pytanie: Jakie są różnice między final, finally i finalize w Javie?

A

final: Modyfikator, który może być używany dla klas, metod i zmiennych, oznaczając, że nie mogą być one dalej modyfikowane.
finally: Blok w strukturze try-catch, który jest zawsze wykonywany po bloku try lub catch.
finalize: Metoda w klasie Object, która jest wywoływana przed usunięciem obiektu przez Garbage Collector.

30
Q

Pytanie: Jakie są główne różnice między Array a List w Kotlinie?

A

W Kotlinie Array i List są dwoma różnymi typami kolekcji, które służą do przechowywania zbiorów elementów. Oto główne różnice między nimi:

  1. Mutability (Zmiennosć):
    • Array jest zawsze mutowalny, co oznacza, że możesz zmieniać elementy na określonych pozycjach, ale jego rozmiar jest stały po inicjalizacji.
    • List może być mutowalna (MutableList) lub niemutowalna (List). MutableList pozwala na zmianę elementów i rozmiaru kolekcji, podczas gdy List jest niemutowalna i nie pozwala na zmianę po jej utworzeniu.
  2. Performance (Wydajność):
    • Array ma potencjalnie lepszą wydajność, ponieważ jest to struktura danych o stałym rozmiarze, która jest bezpośrednio wspierana przez maszynę wirtualną Javy.
    • List jest interfejsem i może mieć różne implementacje, takie jak ArrayList czy LinkedList, które mają różne charakterystyki wydajnościowe.
  3. Primitive Types (Typy Proste):
    • Array może przechowywać typy proste bez opakowywania ich w typy obiektowe (np. IntArray, ByteArray), co jest wydajniejsze.
    • List przechowuje elementy jako obiekty, więc typy proste są automatycznie opakowywane w ich odpowiedniki obiektowe (np. Int w Integer).
  4. Functions and Properties (Funkcje i Właściwości):
    • Array ma wbudowane właściwości i funkcje, takie jak size, get, set.
    • List ma własny zestaw funkcji i właściwości, które są częścią interfejsu Collection, jak size, get, indexOf.
  5. Covariance (Kowariancja):
    • List jest kowariantny w Kotlinie, co oznacza, że jeśli Cat jest podtypem Animal, to List<Cat> jest podtypem List<Animal>.
    • Array w Kotlinie jest inwariantny, co oznacza, że Array<Cat> nie jest podtypem Array<Animal>.

Przykłady użycia:

```kotlin
// Array
val numbersArray: Array<Int> = arrayOf(1, 2, 3)
numbersArray[1] = 5 // Możemy zmienić element, ale nie możemy zmienić rozmiaru</Int>

// List
val numbersList: List<Int> = listOf(1, 2, 3)
// numbersList[1] = 5 // Błąd: nie możemy zmienić elementu, ponieważ jest niemutowalna</Int>

val mutableNumbersList: MutableList<Int> = mutableListOf(1, 2, 3)
mutableNumbersList.add(4) // Możemy dodać element, zmieniając rozmiar listy
~~~</Int>

Wybór między Array a List w Kotlinie zależy od konkretnego przypadku użycia, w tym od wymagań dotyczących wydajności, mutowalności i operacji, które będą wykonywane na kolekcji.

31
Q

Pytanie: Jakie są różnice między RxJava a Kotlin Coroutines?

A

RxJava i Kotlin Coroutines to dwie różne biblioteki do obsługi asynchroniczności i programowania reaktywnego, ale mają różne podejścia i filozofie:

  • Programowanie reaktywne: RxJava jest implementacją wzorca projektowego Reactive Extensions. Umożliwia tworzenie strumieni danych i reagowanie na zmiany w tych strumieniach za pomocą operatorów funkcyjnych.
  • Strumienie: Wszystko jest strumieniem danych, które mogą być obserwowane. Użytkownik subskrybuje te strumienie, aby reagować na elementy, które są emitowane.
  • Backpressure: RxJava ma wbudowane wsparcie dla backpressure, co jest ważne przy obsłudze strumieni danych, które mogą emitować wartości szybciej, niż mogą być przetworzone.
  • Schedulery: RxJava pozwala na precyzyjne zarządzanie wątkami za pomocą Schedulera, co pozwala na łatwe przełączanie między wątkami do operacji wejścia/wyjścia, obliczeń itp.
  • Lekkie wątki: Coroutines są często nazywane “lekkimi wątkami”. Są to kompilowane funkcje, które mogą być zawieszone i wznowione, co pozwala na łatwe i wydajne zarządzanie operacjami asynchronicznymi.
  • Strukturalna współbieżność: Kotlin promuje strukturalną współbieżność, co oznacza, że coroutines są uruchamiane w kontekście, który zarządza ich cyklem życia, co ułatwia zarządzanie zasobami.
  • Integracja z językiem: Coroutines są głęboko zintegrowane z językiem Kotlin i są obsługiwane na poziomie kompilatora, co pozwala na bardziej naturalną składnię i łatwiejsze zarządzanie stanem.
  • Brak wbudowanego wsparcia dla backpressure: W przeciwieństwie do RxJava, coroutines nie mają wbudowanego mechanizmu backpressure, ale można go osiągnąć za pomocą kanałów i przepływów (Flows).

RxJava:
```java
Observable.fromCallable(() -> {
// Długa operacja
return “Wynik”;
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(result -> {
// Aktualizacja UI
}, error -> {
// Obsługa błędu
});
~~~

Kotlin Coroutines:
```kotlin
GlobalScope.launch(Dispatchers.IO) {
val result = withContext(Dispatchers.Default) {
// Długa operacja
“Wynik”
}
withContext(Dispatchers.Main) {
// Aktualizacja UI
}
}
~~~

  • RxJava jest potężnym narzędziem do programowania reaktywnego z dużym ekosystemem operatorów i możliwością obsługi złożonych strumieni danych.
  • Kotlin Coroutines są bardziej zintegrowane z językiem Kotlin i oferują prostszą, bardziej lekką i elastyczną alternatywę do obsługi asynchroniczności.
  • Wybór między nimi zależy od konkretnego przypadku użycia, preferencji i wymagań projektu.
32
Q

Pytanie: Jakie są główne różnice między Kotlin/Native, Kotlin/JS i Kotlin/JVM?

A

Odpowiedź: To są różne backendy kompilatora Kotlin:

Kotlin/JVM: Kompiluje kod Kotlin do bajtkodu JVM.
Kotlin/JS: Kompiluje kod Kotlin do JavaScriptu.
Kotlin/Native: Kompiluje kod Kotlin do natywnego kodu maszynowego dla różnych platform.
33
Q

Pytanie: Jakie są różnice między synchronized w Javie a @Synchronized w Kotlinie?

A

Odpowiedź: W Javie synchronized to modyfikator i blok, który służy do synchronizacji wątków. W Kotlinie @Synchronized to anotacja, która może być używana z funkcjami, aby uczynić je synchronizowanymi.

34
Q

Pamięć używana przez JVM (Java Virtual Machine) w kontekście Garbage Collection (GC)

A

Pamięć używana przez JVM (Java Virtual Machine) w kontekście Garbage Collection (GC) jest podzielona na kilka obszarów. Oto główne segmenty pamięci:

  1. Young Generation (Młoda Generacja):
    • Eden Space: To obszar, w którym obiekty są tworzone po raz pierwszy. Kiedy Eden Space się wypełnia, GC jest uruchamiany. Obiekty, które przeżyły proces GC, są przenoszone do jednego z obszarów Survivor.
    • Survivor Space (S0 i S1): Są to dwa obszary, które przechowują obiekty, które przeżyły po kilku cyklach GC w Eden Space. Obiekty są przenoszone między tymi dwoma obszarami. Gdy obiekt jest wystarczająco “stary”, jest przenoszony do Old Generation.
  2. Old Generation (Stara Generacja):
    • Ten obszar pamięci przechowuje obiekty, które istnieją przez dłuższy czas i przetrwały wiele cykli GC w Young Generation. Proces czyszczenia Old Generation jest zazwyczaj bardziej kosztowny pod względem czasu niż czyszczenie Young Generation, ponieważ obejmuje on większy obszar pamięci i przechowuje obiekty o dłuższym czasie życia.
  3. Permanent Generation (PermGen) / Metaspace (od Java 8):
    • PermGen: W starszych wersjach Javy (przed Java 8) ten obszar pamięci był używany do przechowywania metadanych klas Javy, takich jak informacje o klasach i metodach. Był on ograniczony pod względem wielkości i mógł być przyczyną błędów OutOfMemoryError w przypadku wielu dynamicznie ładowanych klas.
    • Metaspace (od Java 8): W Javie 8 i nowszych, PermGen został zastąpiony przez Metaspace. Metaspace przechowuje metadane klas w natywnej pamięci systemu, a nie w pamięci przydzielonej dla JVM. Dzięki temu jest mniej podatny na błędy związane z wyczerpaniem pamięci, chociaż nadal można napotkać takie błędy w ekstremalnych sytuacjach.
  4. Code Cache:
    • Ten obszar pamięci jest używany do przechowywania kodu maszynowego dla JIT (Just-In-Time) kompilatora. Kiedy JVM kompiluje bajtkod Javy do natywnego kodu maszynowego dla lepszej wydajności, wynikowy kod maszynowy jest przechowywany w Code Cache.

Podział pamięci na te obszary pozwala JVM na bardziej efektywne zarządzanie pamięcią i optymalizację procesu Garbage Collection. Różne algorytmy GC mogą różnie zarządzać tymi obszarami, dlatego ważne jest zrozumienie, jak dany algorytm działa i jakie ma wpływ na wydajność aplikacji.

35
Q

Czym jest JIT?

A

JIT (Just-In-Time) to technika kompilacji używana w wielu systemach wykonawczych, w tym w Java Virtual Machine (JVM). Oto główne cechy i działanie JIT w kontekście Javy:

  1. Podstawowa Idea:
    • Zamiast kompilować cały kod źródłowy na kod maszynowy przed jego wykonaniem (jak w tradycyjnych kompilatorach), JIT kompiluje kod w trakcie jego wykonywania, “właśnie na czas” (stąd nazwa “Just-In-Time”).
  2. Działanie w JVM:
    • Gdy program Java jest kompilowany, jest on przekształcany z kodu źródłowego Javy (.java) do bajtkodu Javy (.class).
    • Bajtkod Javy jest neutralny platformowo i może być wykonywany na dowolnej maszynie z zainstalowanym JVM.
    • Podczas wykonywania programu, zamiast interpretować bajtkod linia po linii (co byłoby wolne), JIT kompiluje często wykonywane fragmenty bajtkodu na natywny kod maszynowy dla konkretnego systemu i procesora. Ten natywny kod maszynowy jest następnie wykonywany bezpośrednio, co znacznie przyspiesza działanie programu.
  3. Optymalizacje:
    • JIT nie tylko kompiluje bajtkod na kod maszynowy, ale również wykonuje wiele optymalizacji podczas kompilacji, takich jak inline’owanie metod, eliminacja martwego kodu czy optymalizacja pętli. Dzięki temu kod wykonuje się szybciej niż w czysto interpretowanym środowisku.
  4. Kompilacja Adaptacyjna:
    • Jednym z kluczowych aspektów JIT w JVM jest adaptacyjna kompilacja. Oznacza to, że JVM monitoruje wykonywanie programu, identyfikuje “gorące punkty” (fragmenty kodu wykonywane bardzo często) i skupia się na kompilacji i optymalizacji tych konkretnych fragmentów. Dzięki temu zasoby są wykorzystywane tam, gdzie przyniosą największe korzyści dla wydajności.
  5. Kompromis pamięci:
    • Chociaż JIT przyspiesza wykonanie programu, wymaga dodatkowej pamięci do przechowywania skompilowanego kodu maszynowego. Jest to kompromis między szybkością a zużyciem pamięci.

Podsumowując, JIT to technika kompilacji używana w JVM, która pozwala na dynamiczną kompilację bajtkodu Javy na natywny kod maszynowy podczas wykonywania programu, co znacznie przyspiesza jego działanie.

36
Q

Co to jest hipoteza generacyjna

A

Hipoteza generacyjna (ang. generational hypothesis) w kontekście zarządzania pamięcią w informatyce, szczególnie w odniesieniu do automatycznego zarządzania pamięcią (takiego jak Garbage Collection w językach programowania takich jak Java), odnosi się do założenia dotyczącego charakterystyki obiektów w pamięci. Kluczowe założenia hipotezy generacyjnej to:

  1. Większość Obiektów Umrze Młodo:
    • Hipoteza ta zakłada, że wiele obiektów staje się “nieosiągalnymi” (czyli kwalifikuje się do usunięcia przez mechanizmy Garbage Collection) niedługo po ich utworzeniu. Innymi słowy, obiekty często mają krótki cykl życia.
  2. Mała Liczba Obiektów Dożywa Starego Wieku:
    • Mniej obiektów przetrwa dłuższy czas. Te, które to robią, często są używane przez dłuższy okres czasu w aplikacji.

W oparciu o tę hipotezę, mechanizmy Garbage Collection w wielu nowoczesnych językach programowania, takich jak Java, są zaprojektowane w taki sposób, aby optymalizować proces usuwania niepotrzebnych obiektów, koncentrując się na młodych obiektach (młodych pokoleniach). To prowadzi do następujących praktyk:

  • Pokoleniowe Zarządzanie Pamięcią (Generational Garbage Collection):
    • Pamięć jest podzielona na kilka pokoleń (zazwyczaj “młode pokolenie” i “stare pokolenie”).
    • Nowo utworzone obiekty są najpierw umieszczane w młodym pokoleniu.
    • Obiekty, które przetrwały kilka cykli zbierania śmieci, są promowane do starszego pokolenia.
    • Garbage Collector częściej czyści młode pokolenie, co jest zazwyczaj mniej kosztowne pod względem wydajności, ponieważ większość obiektów w tym pokoleniu szybko staje się nieosiągalna.

Ta strategia ma na celu optymalizację wydajności zbierania śmieci poprzez minimalizację kosztów związanych z czyszczeniem pamięci. Hipoteza generacyjna jest kluczowym założeniem leżącym u podstaw tej strategii i została potwierdzona empirycznie w wielu realnych aplikacjach.

37
Q
A