1. Усі класи успадковано від Object

Усі класи в Java неявно (приховано) успадковано від класу Object.

Що таке успадкування та як воно працює в Java, ми розберемо у квесті Java Core. А наразі ми розглянемо один простий факт, який із цього випливає:

Змінній типу Object можна присвоїти об'єкт будь-якого класу. Приклад:

Код Примітка
Object o = new Scanner(System.in);
У змінній o збережено посилання на об'єкт типу Scanner
Object o = new String();
У змінній o збережено посилання на об'єкт типу String
Object o = new Integer(15);
У змінній o збережено посилання на об'єкт типу Integer
Object o = "Привіт";
У змінній o збережено посилання на об'єкт типу String

На цьому добрі новини закінчуються. Компілятор не стежить за тим, якого саме типу об'єкт було збережено в змінній типу Object, тому викликати методи, що були у збереженого об'єкта, але відсутні у змінної типу Object, не можна.

Якщо треба викликати методи такого об'єкта, то спочатку посилання на нього потрібно зберегти в змінній правильного типу, а лише потім викликати методи для цієї змінної:

Код Примітка
Object o = new Scanner(System.in);
int x = o.nextInt();
Програма не скомпілюється. Клас Object не має методу nextInt().
Object o = new Scanner(System.in);

Scanner console = (Scanner) o;

int x = console.nextInt();
Так працюватиме.

Тут ми зберігаємо посилання на об'єкт типу Scanner у змінній типу Scanner за допомогою оператора перетворення типу.

Просто так змінну типу Object не можна присвоїти змінній типу Scanner, навіть якщо змінна типу Object зберігає посилання на об'єкт типу Scanner. Натомість це можна зробити, якщо використати вже відомий вам оператор перетворення типу. У загальному випадку це має такий вигляд:

Тип ім'я1 = (Тип) ім'я2;

де ім'я1 — це ім'я змінної типу Тип, а ім'я2 — це ім'я змінної типу Object, яка зберігає посилання на об'єкт типу Тип.

Перетворення типу

Якщо типи змінної та об'єкта не збігаються, виникне помилка ClassCastException. Приклад:

Код Примітка
Object o = new Integer(5);
String s = (String) o;
Під час виконання виникне помилка:
тут буде кинуто виняток ClassCastException

У Java є спосіб обійти цю помилку: існує можливість перевірити, який тип насправді має змінна:

ім'я instanceof Тип

Оператор instanceof перевіряє, чи є змінна ім'я об'єктом типу Тип.

Приклад — пошук рядка серед елементів масиву даних:

Код Примітка
Object[] objects = {10, "Привіт", 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 перетворить ці значення на Integer, String і Double.

Цикл по масиву об'єктів

Якщо об'єкт має тип String,

зберігаємо його в змінній типу String,
виводимо змінну на екран.

18
Задача
Модуль 1. Java Syntax,  26 рівень5 лекція
Недоступна
Файл чи директорія
Напиши програму, яка зчитує рядки з клавіатури та виводить у консоль такі повідомлення: якщо введений рядок є шляхом до існуючого файлу "<уведений рядок> - це файл". Якщо введений рядок є шляхом до існуючої директорії "<уведений рядок> - це директорія". Якщо рядок не є шляхом до файлу чи

2. Причина виникнення шаблонів (колекції)

Повертаємося до колекцій.

Коли Java-розробники тільки створювали клас ArrayList, вони хотіли зробити його універсальним, щоб у ньому можна було зберігати об'єкти будь-якого типу. Тому для зберігання елементів вони скористалися масивом типу Object.

Сильна сторона такого підходу в тому, що в колекцію можна додати об'єкт будь-якого типу.

Ну а слабких сторін одразу декілька.

Недолік 1.

Завжди доводилося писати оператор перетворення типу, коли діставали елементи з колекції:

Код Примітка
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);
}
Створюємо об'єкт-колекцію для зберігання посилань на об'єкти типу Object

Заповнюємо колекцію числами 10, 20, … 100;



Знаходимо суму елементів колекції


Потрібно використовувати перетворення типу

Недолік 2.

Не було гарантії, що в колекції зберігаються елементи певного типу

Код Примітка
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);
}
Створюємо об'єкт-колекцію для зберігання посилань на об'єкти типу Object

Заповнюємо колекцію числами типу Double:
0.0, 2.5, 5.0, ...


Знаходимо суму елементів колекції


Буде помилка: тип Double не можна перетворити на тип Integer

Дані в колекцію можуть вноситися де завгодно:

  • в іншому методі
  • в іншій програмі
  • завантажуватися з файлу
  • отримуватися з мережі

Недолік 3.

Дані колекції можна випадково змінити через незнання.

Ви можете передати колекцію, заповнену своїми даними, в якийсь метод, а цей метод, написаний зовсім іншим програмістом, додасть до вашої колекції свої дані.

З назви колекції незрозуміло, які саме типи даних можна в ній зберігати. Та навіть якщо й дати змінній зрозумілу назву, посилання на неї можна передати в десяток методів, і там уже точно про початкове ім'я змінної нічого не буде відомо.


3. Узагальнення

Узагальнення в Java

Усі ці проблеми усуває така чудова річ у Java, як узагальнення (generics).

Під узагальненнями в Java мають на увазі можливість додавати до типів типи-параметри. Таким чином утворюються складені типи. Такий складений тип у загальному випадку має ось який вигляд:

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

Усе разом — це саме тип. І він може використовуватися там, де зазвичай можна використовувати типи.

Код Опис
ArrayList<Integer> list;
Створення змінних
list = new ArrayList<Integer> ();
Створення об'єктів
ArrayList<Integer>[] array;
Створення масивів

У такій колекції можна зберегти тільки змінні типу Integer:

Код Опис
ArrayList<Integer> list = new ArrayList<Integer>();
list.add(new Integer(1));
list.add(2);
list.add("Привіт");
Колекція типу ArrayList з елементами типу Integer
Так можна
І так можна: працюватиме
autoboxing

А так не можна: помилка компіляції

Як створювати свої класи з типами-параметрами, ви дізнаєтеся у квесті Java Collections. А наразі ми розглянемо, як цим користуватися та як воно працює.


4. Як працюють generics

Насправді generics працюють надзвичайно примітивно.

Компілятор просто замінює тип із параметром на нього ж, тільки без параметра. А при взаємодії з його методами додає операцію перетворення типу на тип-параметр:

Код Що зробить компілятор
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);

Припустімо, ми мали код методу, що підсумовує числа в колекції цілих чисел:

Код Що зробить компілятор
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;
}

Тобто по суті узагальнення — це так само різновид синтаксичного цукру, як і autoboxing, тільки більший. Під час autoboxing компілятор за нас додає методи для перетворення типу int на Integer і навпаки, а для generics додає оператори перетворення типу.

Після того як компілятор скомпілював ваш код з узагальненнями, у ньому всі класи з параметрами буде перетворено на прості класи та оператори перетворення типу. Інформація про те, які типи-параметри спочатку мали змінні складних типів, загубилася. Цей ефект інакше називають стиранням типів.

Іноді програмістам, які пишуть свої класи з типами-параметрами, дуже не вистачає інформації про типи, які туди передаються як параметри. Як із цим борються та що з цього виходить, ви дізнаєтеся у квесті Java Collections.


18
Задача
Модуль 1. Java Syntax,  26 рівень5 лекція
Недоступна
Файлові операції
Напиши програму, яка зчитуватиме з клавіатури два шляхи до файлу. Якщо за першим шляхом файлу немає, його потрібно створити. Якщо за першим шляхом файл є, його потрібно перемістити на другий шлях, але тільки за умови, що за другим шляхом файлу немає. Якщо такий файл є, потрібно просто видалити файл
18
Задача
Модуль 1. Java Syntax,  26 рівень5 лекція
Недоступна
А що там у папці?
Напиши програму, яка зчитує з клавіатури шлях до директорії, отримує список файлів і директорій у вказаній директорії та виводить в консоль інформацію про них у такому вигляді: "<шлях до файлу> - це файл", якщо це файл, "<шлях до директорії> - це директорія", якщо це директорія. Кутові д

5. Кілька фактів про узагальнення

Ще кілька цікавих фактів про узагальнення.

У класів може бути не один тип-параметр, а декілька. Отакий вигляд це має:

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

Власне кажучи, у цьому немає нічого дивного. Там, де компілятор може додати оператор перетворення на один тип, він може додати й декілька таких.

Приклади:

Код Примітка
HashMap<Integer, String> map = new HashMap<Integer, String>();
map.put(7, "Привіт");
map.put(-15, "Привіт");
Перший параметр методу put має тип Integer, другий — тип String

Крім того, складні типи теж можна використовувати як параметри. Отакий вигляд це має:

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

Припустімо, нам треба створити список, який зберігатиме списки рядків. У цьому разі ми отримаємо приблизно такий код:

// список привітань
ArrayList<String> listHello = new ArrayList<String>();
listHello.add("Привіт");
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);