Використання "остаточного" модифікатора, коли це застосовується на Java [закрито]


194

У Java існує практика оголошення кожної змінної (локальної чи класової), остаточного параметра, якщо вони є.

Хоча це робить код набагато більш детальним, це допомагає у легкому читанні / розумінні коду, а також запобігає помилкам, оскільки наміри чітко позначені.

Які ваші думки з цього приводу і що ви слідуєте?


13
Це може призвести до релігійного аргументу. Деяким це подобається, дехто ненавидить. Мені подобаються заключні поля, але не остаточні локальні змінні, якщо вони не потребують, не впевнені, що це цілком раціонально. Не впевнений, що будь-який голосування було б і таким. Я згоден з Алексом Міллером. ;)
Пітер Лорі

Я можу зрозуміти, якщо людям не подобається, щоб їхній код був переповнений фіналом. Але це проблема, яку міг вирішити хороший редактор: bugs.eclipse.org/bugs/show_bug.cgi?id=409379
oberlies

Відповіді:


184

Я думаю, що це стосується гарного стилю кодування. Звичайно, ви можете писати хороші, надійні програми, не використовуючи багато finalмодифікаторів ніде, але коли ви думаєте про це ...

Додавання finalдо всіх речей, які не повинні змінюватися, просто звужує можливості того, що ви (або наступний програміст, працюючи над своїм кодом), неправильно трактуєте або неправомірно подумаєте про процес, який призвів до вашого коду. Принаймні, це повинно дзвонити деяким дзвонам, коли вони зараз хочуть змінити вашу незмінну річ.

Спочатку виглядає незручно бачити finalу вашому коді багато ключових слів, але досить скоро ви перестанете помічати саме слово і просто подумаєте, що-то-ніколи не зміниться-з-за цього пункту- на (ви можете взяти це у мене ;-)

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


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

20
@Timo Це спрацювало б, але є недолік, що це потрібно перевірити після реєстрації, а не, поки розробник працює над кодом. Бо finalкомпілятор, очевидно, не дозволить вам продовжувати, якщо ви помилитесь.
Майк

9
Eclipse має можливість додавати те, finalде це можливо (змінні, які не змінюються), коли ви зберігаєте.
Берт F

22
+1 Я люблю final. Це не має різниці в 98% часу, це незначна незручність% 1 часу, але 1% часу врятує мене від того, щоб робити щось дурне або ненавмисне, добре того варто.
Берт F

14
@BertF Так, я завжди дозволяю Eclipse додавати finalмодифікатори скрізь, коли я " Очищую ..." свій код. Всюди це означає навіть у блоках catch (), і як сказано, ви їх не помітите через деякий час. Звичайно, якщо я б створив мову, це finalбуло б за замовчуванням, а varабо modifiableбуло б необов'язковим ключовим словом.
Maarten Bodewes

191

Ожиріння:

  • Заключні поля - Позначення полів як кінцевих змушує їх встановлювати до кінця побудови, роблячи це посилання непорушним. Це дозволяє безпечно публікувати поля і дозволяє уникнути необхідності синхронізації на наступних зчитуваннях. (Зауважте, що для посилання на об'єкт непорушним є лише посилання на поле - речі, на які посилається посилання на об'єкт, все ще можуть змінюватися, що впливає на незмінність.)
  • Підсумкові статичні поля - Хоча зараз я використовую перерахунки для багатьох випадків, коли я використовував статичні кінцеві поля.

Розгляньте, але використовуйте розумно:

  • Підсумкові класи - Рамка / API-дизайн - це єдиний випадок, коли я це вважаю.
  • Підсумкові методи - В основному такі ж, як і заключні заняття. Якщо ви використовуєте шаблони методів шаблонів, як божевільні та маркування остаточного, ви, ймовірно, занадто багато покладаєтесь на спадщину і недостатньо на делегування.

Ігноруйте, якщо не відчуваєте себе анальним:

  • Параметри методу та локальні змінні - Я РІДНО роблю це значною мірою, тому що я лінивий і мені здається, що це забиває код. Я повністю визнаю, що параметри маркування та локальні змінні, які я не збираюсь змінювати, "правильніші". Я б хотів, щоб це було за замовчуванням. Але це не так, і мені здається, що код важче зрозуміти з усіма фіналами. Якщо я перебуваю в чужому коді, я не збираюся їх витягувати, але якщо я пишу новий код, я не ставлю їх. Один виняток - це випадок, коли вам потрібно позначити щось остаточне, щоб ви могли отримати доступ це з анонімного внутрішнього класу.

8
найкраща відповідь на всі запитання на цю заключну тему
Габріель Шчербак

4
Більшу частину часу, коли я бачу локальну змінну без остаточного слова перед нею, і її там не можна використовувати, це говорить мені, що я, мабуть, повинен витягти якийсь код у новому методі, який повертає потрібне значення, яке я зроблю остаточним. Випадки, коли це не застосовується, - це коли я використовую деякі потоки або інше, що потребує спроб його лову.
nyxz

32

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

Я рекомендую ознайомитись зі статтею, зв'язаною нижче, для отримання більш детальної інформації.

Заключне слово За підсумковим ключовим словом


29

finalМодифікатор, особливо для змінних, є засобом , щоб компілятор дотримання конвенції , яка , як правило , розумно: не переконаєтеся , що (локальний або екземпляр) змінна присвоюється рівно один раз (не більш і не менш). Переконуючись, що змінна обов'язково призначається перед її використанням, ви можете уникнути поширених випадків NullPointerException:

final FileInputStream in;
if(test)
  in = new FileInputStream("foo.txt");
else
  System.out.println("test failed");
in.read(); // Compiler error because variable 'in' might be unassigned

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

 String msg = null;
 for(int i = 0; i < 10; i++) {
     msg = "We are at position " + i;
     System.out.println(msg);
 }
 msg = null;

Вам рекомендується використовувати це:

 for(int i = 0; i < 10; i++) {
     final String msg = "We are at position " + i;
     System.out.println(msg);
 }

Деякі посилання:


1
+1 за дискусію щодо аргументу за кордоном за кордоном
Том Тресанський,

Коротше кажучи, ви можете в основному використовувати finalдля того, щоб зробити Java більш вираженою .
Адам Гент

Насправді "Переконавшись, що змінна обов'язково призначається перед її використанням, ви можете уникнути поширених випадків NullPointerException:" це неправильно, "остаточний" тут не має ніякого значення, компілятор знає і скаржиться на змінну "в", можливо будучи недійсним у наведеному прикладі.
nyholku

@nyholku Ви маєте рацію, в Java локальні змінні повинні бути визначені перед використанням, також без остаточних. Але для полів вам потрібна остаточна. У дещо складнішому прикладі, коли "in" - це змінна інстанція класу, а якщо / else є в конструкторі, вам потрібно остаточний сигнал для проблеми. Крім того, також для локальних змінних є деяке значення у підсумковому IMO, оскільки це заважає неохайному програмісту "виправити" помилку компіляції про "в", можливо, не призначивши, додавши "= null" до своєї декларації. Іншими словами, заключні змінні зменшують використання нуля, на мій досвід.
Бруно Де

20

Я досить догматично стосується оголошення кожної можливої ​​змінної final. Сюди входять параметри методу, локальні змінні та рідко значення об'єктних полів. У мене є три основні причини декларування кінцевих змінних скрізь:

  1. Декларація наміру: Декларуючи остаточну змінну, я заявляю, що ця змінна повинна бути записана лише один раз. Це тонкий підказ для інших розробників і великий натяк на компілятор.
  2. Застосування змінних для одноразового використання: Я вірю в ідею, що кожна змінна повинна мати лише одну мету в житті. Даючи кожній змінній лише одну мету, ви скорочуєте час, необхідний для визначення цілі цієї змінної під час налагодження.
  3. Дозволяє оптимізувати: я знаю, що компілятор використовував трюки з підвищення продуктивності, які конкретно покладалися на незмінність змінної посилання. Мені подобається думати, що деякі з цих старих хитрощів (або нових) компілятор буде використаний.

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


2
finalзмінні та параметри методу не впливають на продуктивність, оскільки вони не виражаються в байт-коді.
Стів Куо

змінна: = латина: varius> різноманітна> модифікується
dieter

17

Ефективна Java має пункт, який говорить "Улюблені незмінні об'єкти". Оголошення полів як кінцевих допомагає зробити невеликі кроки до цього, але, звичайно, є набагато більше справді непорушних об’єктів, ніж це.

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


15
обережне - остаточне і незмінне абсолютно різні поняття. Остаточно легко мати кінцевий об'єкт, що змінюється - остаточний стосується довідки, мутаційність - про екземпляр об'єкта. кінцева особа olaf = нова особа (); olaf.setName ("Олаф");
Олаф Кок,

1
Точно - незмінні об'єкти - це одне, незмінні посилання (тобто остаточні) - це зовсім інше.
SCdF

2
Але вони тісно пов'язані, оскільки ви можете, оголосивши поле Person.name як остаточне (і оголосивши клас остаточним, і ...) зробити об'єкт Person остаточним. Однак це не так просто, як просто робити "остаточну особу" ...
Себастьян Гансландт

Це також не має значення для локальних змінних (параметри - локальні). Це стосується лише змінних класів, тому воно лише частково вирішує питання.
Робін

12

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

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

Якщо, однак, ви не вважаєте, що це ускладнює читання коду чи довше його писати, то, будь-ласка, піти на це.

Редагувати: Як уточнення (і спроба відмовитися від голосування), я не кажу, що не позначайте константи як остаточні, я кажу, що не робіть такі речі, як:

public String doSomething() {
  final String first = someReallyComplicatedExpressionToGetTheString();
  final String second = anotherReallyComplicatedExpressionToGetAnother();

  return first+second;
}

Це просто робить код (на мій погляд) важчим для читання.

Варто також пам’ятати, що все остаточне завдання - це не дозволяти вам перепризначити змінну, це не робить її непорушною чи щось подібне.


1
Що стосується заяви на голові: я був в багатьох ситуацій , в яких НЕ використовуючи final(і незмінні об'єкти в цілому) були істотний фактор , що сприяє число і впливу помилок.
Кріс Вест

3
Я все за незмінні об'єкти, я просто ніколи не потрапляв у ситуацію, коли маркування незмінного об'єкта фіналом мені допомогло.
SCdF

2
Я погоджуюся, що ми не повинні використовувати final для локальних змінних та параметрів методу, це робить код набагато менш читабельним.
rmaruszewski

@ChrisVest, можливо, ваші функції занадто довгі
Pacerier

8

Заключні завжди повинні використовуватися для констант. Це навіть корисно для короткочасних змінних (в рамках одного методу), коли правила визначення змінної є складними.

Наприклад:

final int foo;
if (a)
    foo = 1;
else if (b)
    foo = 2;
else if (c)
    foo = 3;
if (d)        // Compile error:  forgot the 'else'
    foo = 4;
else
    foo = -1;

6

Здається, один з найбільших аргументів проти використання кінцевого ключового слова полягає в тому, що "це непотрібно", і це "витрачає місце".

Якщо ми визнаємо багато переваг «остаточного», на що вказують багато чудових публікацій, хоча, визнаючи, що це вимагає більше типізації та місця, я б заперечував, що Java повинна була зробити змінні «остаточними» за замовчуванням і вимагати, щоб все було позначено » "", якщо кодер хоче, щоб це було.


1
Покірники, хочете пояснити?
RAY

Тоді здається дивним називати це змінною, чи не так?
Алан

2
Є тисячі англійських слів на вибір.
RAY

Це прекрасно. Не існує аргументів проти final"занадто довго писати", "робить код незграбним". Існує кілька аргументів для final. І ми можемо автоматично додати його, якщо ви не хочете вводити його вручну.
Дмитро Попов

5

Я finalвесь час використовую для атрибутів об'єкта.

finalКлючове слово має видимість семантику при використанні атрибутів об'єкта. В основному, встановлення значення кінцевого атрибута об'єкта відбувається, перш ніж конструктор повернеться. Це означає, що доки ви не дозволите, щоб thisпосилання не виходила з конструктора, а ви використовували finalдля всіх атрибутів, ваш об’єкт (під семантикою Java 5) гарантовано належним чином сконструйований, і оскільки він є незмінним, його можна безпечно опублікувати до інших ниток.

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

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

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


4

Я повинен прочитати багато коду для своєї роботи. Відсутність остаточних змінних екземплярів - одна з найкращих речей, яка мене дратує і робить необмежене розуміння коду. За мої гроші, остаточний показник локальних змінних викликає більшу загрозу, ніж ясність. Мова повинна була бути розроблена таким чином, щоб це було за замовчуванням, але ми повинні жити з помилкою. Іноді це корисно, особливо для циклів і певного призначення з деревом if-else, але, як правило, це означає, що ваш метод занадто складний.


Чому б не використовувати інструмент, що зменшує захаращення?
Печер'є

@Pacerier Як у редакторі, який неправильно представляє код?
Том Хотін - тайклін

3

final, очевидно, слід використовувати на константах і для забезпечення незмінності, але є ще одне важливе використання методів.

Ефективна Java має цілий пункт цього пункту (Пункт 15), який вказує на підводні камені ненавмисного успадкування. Ефективно, якщо ви не спроектували та не задокументували свій клас на спадщину, успадкування від нього може спричинити несподівані проблеми (елемент дає хороший приклад). Тому рекомендується використовувати остаточний для будь-якого класу та / або методу, від якого не передбачалося успадковувати.

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


3

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


І цей пост є великим доказом цього.
inigoD

2

Навіть для локальних змінних, знаючи, що це оголошено остаточним, означає, що мені не потрібно турбуватися про те, що посилання буде змінено згодом. Це означає, що під час налагодження і я бачу цю змінну пізніше, я впевнений, що вона посилається на той самий об’єкт. Це одне менше, про що я повинен переживати, шукаючи помилку. Бонусом є те, що якщо 99% змінних оголошені остаточними, то декілька змінних, які дійсно є змінними, виділяються краще. Крім того, фінал дозволяє компілятору знайти кілька можливих дурних помилок, які в іншому випадку можуть залишитися непоміченими.


2

Вибір типу finalдля кожного параметра у кожному методі викликає стільки роздратування як у кодерів, так і у читачів кодів.

Як тільки роздратування виходить за рамки розумного переходу на Scala, де аргументи остаточні за замовчуванням.

Або ви завжди можете використовувати інструменти для створення коду, які будуть робити це автоматично для вас. Усі IDE мають їх реалізовані або як плагіни.


1

Остаточний при використанні зі змінними в Java надає заміну константі в C ++. Отже, коли остаточна та статична використовуються для змінної, вона стає непорушною. У той же час робить міграційних програмістів C ++ досить щасливими ;-)

При використанні з опорними змінними це не дозволяє повторно посилатись на об'єкт, хоча об'єктом можна керувати.

Коли final використовується з методом, він не дозволяє перекрити метод підкласами.

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

Слід використовувати його лише для змінних, коли ти чорт упевнений, що значення змінної буде / ніколи не змінюватиметься. Також переконайтеся, що ви дотримуєтесь конвенції кодування, заохоченої SUN.for наприклад: final int COLOR_RED = 1; (Верхній регістр відокремлений підкресленням)

За допомогою посилальної змінної використовуйте її лише тоді, коли нам потрібна незмінна посилання на певний об'єкт.

Що стосується читабельності, то випливає, що коментарі відіграють дуже важливу роль при використанні остаточного модифікатора.


Може хтось скаже мені, чому за це проголосували ??? Просто цікаво ..
Всемогутній

Це, мабуть, тому, що ви заявляєте, що ви повинні ТІЛЬКИ зробити його остаточним, коли XYZ Інші вважають кращою практикою робити ВСЕ фінальне, якщо немає необхідності робити це інакше.
Програматор поза законом

1
Це зовсім не заміна ключового слова C ++ const. -1.
tmj

1

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

Приблизно те ж саме стосується і змінних членів, оскільки вони надають невелику користь, за винятком випадків констант.

Це також не має відношення до незмінності, оскільки найкращим показником того, що щось незмінне, є те, що воно задокументоване як таке та / або не має методів, які могли б змінити об'єкт (це разом з тим, щоб зробити клас остаточним - єдиний спосіб гарантувати, що воно незмінне).

Але е, це лише моя думка :-)


1

Я налаштував Eclipse, щоб додати остаточне значення для всіх полів та атрибутів, які не змінені. Це чудово працює, використовуючи Eclipse "збереження дій", який додає ці остаточні модифікатори (крім іншого) при збереженні файлу.

Настійно рекомендується.

Перегляньте мій пост блогу Eclipse Save Actions.


1

Для аргументів я думаю, що вони не потрібні. Мостлі вони просто шкодять читальності. Перепризначення змінної аргументу настільки шалено дурне, що я повинен бути впевнений, що до них можна ставитися як до констант.

Той факт, що кольори Eclipse підсумковий червоний, полегшує виявлення змінних оголошень у коді, що, на мою думку, покращує готовність більшості часу.

Я намагаюся застосувати правило про те, що будь-які і всі змінні повинні бути остаточними, це не є важка причина для того, щоб це не було. Так набагато простіше відповісти на "що це за змінна?" питання, чи вам просто потрібно знайти ініціалізацію і бути впевненим, що це все.

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

Остаточна змінна - це просто приємний спосіб міряти значення.

Не остаточна змінна пов'язана з частиною алгоритму, схильного до помилок.

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


1

Я кодую деякий час і використовую остаточний, коли тільки можу. Зробивши це деякий час (для змінних, параметрів методу та атрибутів класу), я можу сказати, що 90% (або більше) моїх змінних насправді є остаточними. Я думаю, що користь від того, щоб НЕ було змінено змінні, коли ви не хочете (я бачив, що раніше, і це болить іноді), платить за додаткове введення тексту та додаткові "остаточні" ключові слова у вашому коді.

При цьому, якщо я б створив мову, я би зробив кожну змінну остаточним, якщо не було змінено іншим ключовим словом.

Я не використовую фінал дуже багато для занять і методів, подумав. Це більш-менш складний вибір дизайну, якщо тільки ваш клас не є корисним класом (у такому випадку у вас повинен бути лише один приватний конструктор).

Я також використовую Collections.unmodifiable ... для створення немодифікованих списків, коли мені потрібно.


0

Використання анонімних місцевих класів для слухачів подій, і таке є загальною схемою в Java. Найбільш поширене використання кінцевого ключового слова - переконатися, що змінні за обсягом є доступними для рівного слухача.

Однак, якщо вам здається, що вам потрібно ввести багато остаточних тверджень у свій код. Це може бути хорошим підказом, що ви робите щось не так.

Стаття, розміщена вище, дає такий приклад:

public void doSomething(int i, int j) {
    final int n = i + j; // must be declared final

    Comparator comp = new Comparator() {
        public int compare(Object left, Object right) {
            return n; // return copy of a local variable
        }
    };
}

0

Я використовую його для констант методів всередині і зовні.

Я інколи використовую його для методів, тому що я не знаю, чи підклас НЕ хотів би замінити даний метод (з будь-яких причин).

Що стосується класів, то лише для деяких класів інфраструктури я використовував заключний клас.

IntelliJ IDEA попереджає вас, якщо в функцію записаний параметр функції. Отже, я перестав використовувати final для аргументів функції. Я також не бачу їх у java Runtime library.


0

Я навряд чи використовую фінал на методах чи заняттях, тому що люблю дозволяти людям їх перекривати.

В іншому випадку я використовую, нарешті, лише якщо це public/private static final type SOME_CONSTANT;


Хм..редаговано в одному місці .. все-таки сказано, нарешті, на другому рядку ;-)
Всемогутній

Дозволити людям переоцінювати значення - це єдине найбільше джерело сюрпризів і важко розгадати помилки
RAY

0

Позначення остаточного класу може також зробити так, щоб деякі прив'язки методу відбувалися під час компіляції замість виконання. Розглянемо "v2.foo ()" нижче - компілятор знає, що B не може мати підклас, тому foo () не може бути замінено, тому реалізація для виклику відома під час компіляції. Якщо клас B НЕ позначений остаточним, можливо, фактичний тип v2 - це якийсь клас, який розширює B і переосмислює foo ().

class A {
    void foo() {
        //do something
    }
}
final class B extends A {
    void foo() {
    }
}
class Test {
    public void t(A v1, B v2) {
        v1.foo();
        v2.foo();
    }
}

-1

Настійно рекомендується використовувати фінал для констант . Однак я б не використовував це для методів чи класів (або принаймні продумував це деякий час), оскільки це робить тестування складнішим, якщо не неможливим. Якщо ви абсолютно повинні зробити клас чи метод остаточним, переконайтеся, що цей клас реалізує деякий інтерфейс, щоб ви могли мати макет, реалізуючи той самий інтерфейс.


Це не ускладнює тестування, оскільки ви все одно повинні використовувати інтерфейси.
Кріс Вест
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.