Канонічна форма класів Java

Оригінал статті розміщений Joseph Bergin, 13 вересня 2009

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

public class Name extends Object implements Cloneable, java.io.Serializable
{ 	public Name()
	{	... DO NOT call non final methods from here.
	}

	public String toString()
	{	return ... 
	}

	public boolean equals(Object o)
	{	... Defines an Equivalence relation
			Reflexive: a.equals(a) must return true.
			Symmetric: If a.equals(o) then o.equals(a) as well. 
			Transitive: If a.equals(b) and b.equals(c), then a.equals(c).
		... Note that Symmetric is usually the difficult one to be sure of. 
	}

	public int hashCode()
	{	... Consistent with equals: two "equals" objects should have same hashCode result.
	}
}

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

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

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

Основний зміст рівнянь виглядає так:

public boolean equals(Object o)
{	if(o == null) return false;
	if(getClass() != o.getClass()) return false;
	return ... depending on fields of o and this. 
}

Об’єкт для виконання рівнянь використовує == що засилається на рівність. Зазвичай це створює неправильне зіставлення, поки не отримає для розгляду значення об’єкта. За допомогою hashCode методу об’єкт може бути розміщений в хєш таблицю (Hashtable і HashMap) у формі символів. Якщо hashCode не може поєднуватись з рівняннями, то хєш таблиця також не може бути використана. Але якщо ж тест рівнянь є базовим в певних робочих сферах класу, то hashCode буде базуватися на тих самих сферах. (Дякую Оуену Астрахану із університету Дюк, за вказівки на помилки в ранніх версіях.)

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

Більша частина класів має використовувати java.io.Serializable, котра відповідає всім технічним умовам. Це допомагає зберегти об’єкт в файл чи передати його по мережі.

Також більша частина класів має використовувати Cloneable і метод відновлення справжніх копій:

	public Object clone()
	{	...
	}

Загалом цей метод є захищеним якщо відбувається використання CloneNotSupportedException

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

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

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

public int compareTo(Object other)

public int compareTo(Object other)
{	...
}

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

Так як і в Java 5 більша частина класів (принаймні ті об’єкти, котрі можна відсортувати), мають використовувати Comparable інтерфейс (із java.lang).

	class Foo implements Comparable<Foo>{
		int compareTo(Foo value){
			...
		}
	} 
 

Ці об’єкти класу зрівнюються один з одним. Якщо ж є можливість, що хтось з них "менше" чим b то a.compareTo(b) котре отримує негативне значення. Загалом якщо a. рівняє (b), то a.compareTo(b) отримуючи нуль. Або наприклад a.compareTo(b) отримуючи протилежне значення b.compareTo(a). Фактично compareTo має сформувати еквівалентне відношення в значеннях (симетричності, перехідності та рефлексивності) так само як і найбільше значення.

А методи java.util.Collections в свою чергу, можуть сортувати елементи автоматично, в той час як вони завантажуються і тому подібне. Також, доволі просто помістити їх в відсортований список по типу реєстру, не виконуючи для цього зайвих дій.