1. Wszystkie klasy są dziedziczone zObject

Wszystkie klasy w Javie są niejawnie (potajemnie) dziedziczone z Object.

Czym jest dziedziczenie i jak działa w Javie, przeanalizujemy w zadaniu Java Core. Teraz rozważymy jeden prosty fakt, który z tego wynika:

Zmiennej typu Objectmożna przypisać obiekt dowolnej klasy. Przykład:

Kod Notatka
Object o = new Scanner(System.in);
oOdwołanie do obiektu typu jest przechowywane w zmiennejScanner
Object o = new String();
oOdwołanie do obiektu typu jest przechowywane w zmiennejString
Object o = new Integer(15);
oOdwołanie do obiektu typu jest przechowywane w zmiennejInteger
Object o = "Cześć";
oOdwołanie do obiektu typu jest przechowywane w zmiennejString

Na tym kończą się dobre wieści. Kompilator nie śledzi dokładnie, jaki typ obiektu był przechowywany w zmiennej typu Object, dlatego nie można wywołać metod, które miał przechowywany obiekt, ale których nie ma zmienna Object typu .

Jeśli potrzebujesz wywołać metody takiego obiektu, to najpierw musisz zapisać do niego odwołanie w zmiennej odpowiedniego typu, a dopiero potem wywołać metody na tej zmiennej:

Kod Notatka
Object o = new Scanner(System.in);
int x = o.nextInt();
Program nie skompiluje się. Klasa Objectnie ma metody nextInt().
Object o = new Scanner(System.in);

Scanner console = (Scanner) o;

int x = console.nextInt();
Tak to będzie działać.

Tutaj zapisujemy odwołanie do obiektu typu Scannerdo zmiennej typu Scannerza pomocą operatora rzutowania .

Tylko dlatego, że zmiennej typu Objectnie można przypisać do zmiennej typu Scanner, nawet jeśli zmienna typu Objectprzechowuje odwołanie do obiektu typu Scanner. Ale można to zrobić, jeśli użyjesz znanego ci operatora rzutowania typów . Ogólnie wygląda to tak:

Тип Nazwa1 = (Тип) Nazwa2;

Gdzie Nazwa1 jest nazwą zmiennej typu Типi Nazwa2 jest nazwą zmiennej typu Object, która zawiera odwołanie do obiektu typu Тип.

Odlewanie typu

Jeśli typy zmiennej i obiektu nie pasują do siebie, wystąpi błąd ClassCastException. Przykład:

Kod Notatka
Object o = new Integer(5);
String s = (String) o;
Podczas wykonywania wystąpi błąd:
tutaj zostanie zgłoszony wyjątekClassCastException

Java ma sposób na obejście tego błędu: istnieje sposób sprawdzenia, jaki typ faktycznie znajduje się w zmiennej :

Nazwa instanceof Тип

Operator instanceofsprawdza, czy zmienna jest Nazwa obiektem typu Тип.

Przykładem jest znalezienie ciągu znaków wśród tablicy danych:

Kod Notatka
Object[] objects = {10, "Cześć", 3.14};

for (int i = 0; i < objects.length; i++)
{
   if (objects[i] instanceof String)
   {
      String s = (String) objects[i];
      System.out.println(s);
   }
}
Autoboxing zamieni te wartości w Integer, Stringi Double.

Przejrzyj tablicę obiektów

Jeśli obiekt ma typ String

Zapisz go jako zmienną typu String
Wyświetl zmienną na ekranie.


2. Przyczyna występowania wzorców (kolekcji)

Powrót do kolekcji.

Gdy programiści Java po raz pierwszy stworzyli tę klasę ArrayList, chcieli uczynić ją ogólną, tak aby mogła przechowywać obiekty dowolnego typu. Dlatego do przechowywania elementów użyli tablicy typu Object.

Siła tego podejścia polega na tym, że do kolekcji można dodać obiekt dowolnego typu.

Cóż, jest kilka słabych.

Wada 1.

Zawsze musiałem napisać operator konwersji typu podczas pobierania elementów z kolekcji:

Kod Notatka
ArrayList numbers = new ArrayList();


for (int i = 0; i < 10; i++)
   numbers.add(i * 10);


int sum = 0;
for (int i = 0; i < 10; i++)
{
   sum = sum + (Integer) numbers.get(i);
}
Utwórz obiekt kolekcji do przechowywania odwołań do obiektów typu Object

Wypełnij kolekcję liczbami 10, 20, ... 100;



Podsumuj elementy kolekcji


Musisz użyć rzutowania typów

Wada 2.

Nie było gwarancji, że w kolekcji przechowywane są elementy określonego typu

Kod Notatka
ArrayList numbers = new ArrayList();


for (int i = 0; i < 10; i++)
   numbers.add(i * 2.5);


int sum = 0;
for (int i = 0; i < 10; i++)
{
   sum = sum + (Integer) numbers.get(i);
}
Tworzymy obiekt kolekcji do przechowywania referencji do obiektów typu Object

Wypełnij zbiór liczbami typu Double:
0.0, 2.5, 5.0, ...


Sumuj elementy zbioru


Wystąpi błąd: Doublenie można rzutować typu na typInteger

Dane w zbiorze można uzupełnić w dowolnym miejscu:

  • w innej metodzie
  • w innym programie
  • załadować z pliku
  • odbierać przez sieć

Wada 3.

Dane kolekcji mogą zostać przypadkowo zmienione z powodu niewiedzy.

Możesz przekazać kolekcję wypełnioną Twoimi danymi do jakiejś metody, a ta metoda, napisana przez zupełnie innego programistę, doda swoje dane do Twojej kolekcji.

Z nazwy kolekcji nie jest jasne, jakie typy danych mogą być w niej przechowywane. I nawet jeśli nadasz zmiennej taką nazwę, referencja do niej może zostać przekazana do kilkunastu metod, a tam nic nie będzie wiadomo o oryginalnej nazwie zmiennej.


3. Leki generyczne

Generyki w Javie

Wszystkie te problemy są eliminowane przez tak fajną rzecz w Javie, jak generyczne (Generics).

Rodzaje w Javie oznaczają możliwość dodawania typów parametrów do typów. W ten sposób uzyskuje się złożone typy złożone. Taki typ złożony wygląda ogólnie tak:

ОсновнойТип<ТипПараметр>

Wszystko razem - to jest dokładnie ten typ. I może być używany tam, gdzie normalnie używałbyś typów.

Kod Opis
ArrayList<Integer> list;
Tworzenie zmiennych
list = new ArrayList<Integer> ();
Tworzenie obiektów
ArrayList<Integer>[] array;
Tworzenie tablic

W takim zbiorze mogą być przechowywane tylko zmienne typu Integer:

Kod Opis
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(new Integer(1));
list.add(2);
list.add("Cześć");
Kolekcja typu ArrayListz elementami typu Integer
So you can
I so you can: zadziała
autoboksowanie

To niemożliwe: błąd kompilacji

Dowiesz się, jak tworzyć własne klasy z typami parametrów w zadaniu Java Collections. Teraz przeanalizujemy, jak go używać i jak działa.


4. Jak działają leki generyczne

W rzeczywistości leki generyczne działają w strasznie prymitywny sposób.

Kompilator po prostu zastępuje typ parametrem typem bez parametru. A podczas interakcji ze swoimi metodami dodaje operację rzutowania typu do typu parametru:

Kod Co zrobi kompilator
ArrayList<Integer> list = new ArrayList<Integer>();
ArrayList list = new ArrayList();
list.add(1);
list.add( (Integer) 1 );
int x = list.get(0);
int x = (Integer) list.get(0);
list.set(0, 10);
list.set(0, (Integer) 10);

Powiedzmy, że mamy kod metody, która sumuje liczby w zbiorze liczb całkowitych:

Kod Co zrobi kompilator
public int sum(ArrayList<Integer> numbers)
{
   int result = 0;

   for (int i = 0; i < numbers.size(); i++)
      result = result + numbers.get(i);

   return result;
}
public int sum(ArrayList numbers)
{
   int result = 0;

   for (int i = 0; i < numbers.size(); i++)
      result = result + (Integer) numbers.get(i);

   return result;
}

Te. w rzeczywistości generyczne są rodzajem cukru składniowego, jak autoboxing, tylko więcej. Dzięki autoboxingowi kompilator dodaje dla nas metody konwersji typów intdo Integeriz powrotem, a dla typów generycznych dodaje operatory rzutowania typów.

Po tym, jak kompilator skompiluje twój kod z typami ogólnymi, wszystkie klasy z parametrami zostały przekonwertowane na same klasy i operatory rzutowania. Informacje o tym, jakie typy-parametry były pierwotnie dla zmiennych typów złożonych, zostały utracone. Ten efekt jest również nazywany wymazywaniem czcionek .

Czasami programistom, którzy piszą swoje klasy z typami parametrów, bardzo brakuje informacji o typach, które są tam przekazywane jako parametry. Jak sobie z tym poradzić i co z tego wyniknie, dowiesz się w zadaniu Java Collections.



5. Kilka faktów na temat leków generycznych

Kilka ciekawszych faktów na temat leków generycznych.

Klasy mogą mieć więcej niż jeden typ parametru, ale kilka. Wygląda to mniej więcej tak:

ОсновнойТип<ТипПараметр1, ТипПараметр2, ТипПараметр3>

Ściśle mówiąc, nie jest to zaskakujące. Tam, gdzie kompilator może dodać operatora rzutowania do jednego typu, może dodać więcej niż jeden.

Przykłady:

Kod Notatka
HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put(7, "Cześć");
map.put(-15, "Cześć");
pierwszy parametr metody putjest typu Integer, drugi jest typuString

Również typy złożone mogą być również używane jako parametry . Wygląda to mniej więcej tak:

ОсновнойТип<ТипПараметр<ТипПараметрПараметра>>

Powiedzmy, że chcemy utworzyć listę, która będzie przechowywać listy ciągów znaków. W takim przypadku otrzymamy coś takiego jak ten kod:

// список приветствий
ArrayList<String> listHello = new ArrayList<String>();
listHello.add("Cześć");
listHello.add("Hi");

// список прощаний
ArrayList<String> listBye = new ArrayList<String>();
listBye.add("Пока");
listBye.add("Good Bye");

// список списков
ArrayList<ArrayList<String>> lists = new ArrayList<ArrayList<String>>();
lists.add(listHello);
lists.add(listBye);