Class 'ві Class' и

Вступна
Напевно, java-класи — це найвідоміша її частина. Ми їх використовуємо щодня, пишемо їх, правимо їх. Але є багато нюансів, про які ми навіть не здогадуємося. І я люблю за це 'нашу' java — вона завжди зможе залишатися загадковою, таємничою. Сьогодні частина її секретів впаде до Ваших ніг. Тут ви знайдете незвичайні приклади коду, смішну історію і цікаву статистику. Кому цікаво, ласкаво просимо під кат.

Трохи деталей
Якщо Ви java-експерт, приклади коду для Вас будуть нудними, а в іншому, як завжди. У свій час мені було дуже цікаво, що в java 7 зовні і під капотом, як влаштований формат файлу class та інше. Мені довелося познайомитися ось із цими документами. Від туди я підкреслив майже всі ідеї для цієї статті. Тим не менш, я заздалегідь прошу вибачення за неточності в термінології у фундаментальних теоретиків і досвідчених експертів. На деякі питання я не буду давати відповіді з-за їх очевидності або легкого пошуку відповіді.

І так перше питання: 'А які бувають типи і види класів java 7?' Більшість відповість правильно, але деякі — неповністю. Дуже часто забувають згадати про локальні класи.

Локальний клас
Я не знайшов швидко хорошого визначення локального класу Російською мовою, а з Англійською проблеми, тому своїми словами: 'Локальний клас — це внутрішній і вкладений в клас, який не є членом іншого класу і оголошення якого здійснюється всередині блоку коду або методу'. Трохи заплутано? На прикладі все просто:

Приклад локального класу
public class LocalClassExample {
{
// локальний клас у блоці ініціалізації
class MyFirstLocalClass {
int someField;
};
}
// в методі
public void someMethod() {
// ще один
class MySecondLocalClass {
};
}
// в статичному методі
public static void someStaticMethod() {
class MyThirdLocalClass {
};
}
// і навіть так
public void someBlock() {
try {
} catch (Exception e) {
class MyFourthLocalClass {};
}
}
}

Я переглянув дуже багато коду за своє життя, але ні разу не зустрів явних іменованих декларацій класу усередині методу. Може просто не пощастило. А Ви зустрічали? Але коли я вирішив зібрати статистику за типами та видами класів, то виявив, що в rt.jar локальні класи присутні і більш того, використовуються у такому відомому класі як java.lang.Package. Вік живи — вік учись. Ще є цікаве твердження: 'Анонімний клас — це локальний клас без імені'. Для експертів питання: 'Так це?'

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

@Target(ElementType.LOCAL_VARIABLE)
@Retention(RetentionPolicy.RUNTIME)
public @interface SmileAnn {
String name() default "";
}

Але тим не менш, тут є чому здивуватися. Як ви думаєте, нижче представлений валідний код?

Дивний код анотації
@Target(ElementType.LOCAL_VARIABLE)
@Retention(RetentionPolicy.RUNTIME)
public @interface DontSmileAnn {
String name() default "";
/** Що це? */
static final String WHAT1 = "WHAT1";
/** А це? */
final String WHAT2 = "WHAT2";
/** Хто дозволив тут клас оголосити? */
static class What3 {
};
}

Насправді нічого складного немає. Але продовжимо, а як Вам такий приклад?

Спадкоємець
public class ExtendsFromAnn implements DontSmileAnn {
@Override
public Class<ExtendsFromAnn> annotationType() {
return ExtendsFromAnn.class;
}
@Override
public String name() {
return "ExtendsFromAnn";
}
}

Відповіді тут всі прості — це робочі приклади коду, так як за фактом, під капотом interface = interface, з невеликими застереженнями, тому все, що можна писати в interface можна і в анотаціях (знову ж таки із застереженнями). Спадкування в коді від класу типу анотація я зустрічав в тестах. Про анотації у мене все, але є маленький приклад анотованого типу масиву рядків і форми його оголошення:

Незвичайна форма
public static void main(String[] args) {
// Так можна, але все одно дивно.
@SmileAnn String []simpleArray[] = {{}};
}

Сподіваюся я Вас не втомив. Але якщо це не так, тоді наступний абзац спеціально для Вас.

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

Одного разу розробнику потрібно написати утилитный клас для вирішення поставленого завдання. Начебто все зробив правильно, написав java-doc, тести. Відправив патч на рев'ю.

/**java-doc*/
public class Utils {
/**кілька методів, для економії місця не наводжу*/
}

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

/**java-doc*/
public class Utils {
/** Добрий коментар від доброго розробника */
private Utils() {}
/**кілька методів, для економії місця не наводжу*/
}

'Готовий' — крикнув розроблення дизайн, 'Молодець' — відповів михайло Михайлович. Хотів він було натиснути submit, як пролунав дзвінок. В цей самий момент, начальник департаменту, звільнившись від важливих справ вирішив труснути старовиною і тицьнув у перший-ліпший патч для рев'ю. 'О-О-О!' — завищав він. 'Михайлович, Ви що код розучилися писати? А де захист від деб*ла?'. Начальник департаменту людина серйозна, тому Михайлович про себе: 'Що у нас в компанії, деб*ли працюють або ти когось конкретно маєш на увазі?". Похмурий Михайлович загортає патч з позначкою додати abstract до класу. Нижня губа разраба затряслася.Шо, знову?

/**java-doc і дуже милий коментар від милого розробника */
public abstract class Utils {
/** Добрий коментар від доброго розробника */
private Utils() {}
/**кілька методів, для економії місця не наводжу*/
}

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

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

public class EnumExample {
public enum E1 {
SIMPLE
}
public enum E2 {
SIMPLE()
}
public enum E3 {
SIMPLE {
}
}
public enum E4 {
SIMPLE() {
}
}
}

Якщо ви знаєте правильну відповідь, то наступне питання Вам здасться легким: 'Що буде на консолі?'

public class EnumExample {
public enum E1 {
SIMPLE
}
public enum E2 {
SIMPLE()
}
public enum E3 {
SIMPLE {
}
}
public enum E4 {
SIMPLE() {
}
}
public static void main(String[] args) {
System.out.println(E1.SIMPLE.getClass().isEnum());
System.out.println(E2.SIMPLE.getClass().isEnum());
System.out.println(E3.SIMPLE.getClass().isEnum());
System.out.println(E4.SIMPLE.getClass().isEnum());
}
}

Звичайно, тут все на поверхні — E3.SIMPLE і E4.SIMPLE це екземпляри анонімного класу цих енумов. Тому останні 2 виклику дадуть false результат. Будьте уважні, коли використовуєте перевірку на enum клас через isEnum().

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

Внутрішній внутрішній клас
// Файл InnerClassExample.java
public class InnerClassExample {
private int myField;
public class InnerClass {
private int myField;
public class InnerInnerClass {
private int myField;
public InnerInnerClass() {
}
}
}
}
// Файл InnerClassCreate.java
public class InnerClassCreate {
public static void main(String[] args) {
}
}

І перше питання: 'Як отримати доступ до поля myField класу InnerClassExample в конструкторі класу InnerInnerClass і чи це можливо?' Друге питання: 'Як створити екземпляр класу InnerInnerClass в методі main класу InnerClassCreate?

Відповідь
public class InnerClassExample {
private int myField;
public class InnerClass {
private int myField;
public class InnerInnerClass {
private int myField;
public InnerInnerClass() {
int mf1 = InnerClassExample.this.myField; // Відповідь: так.
// Ще цікаві приклади:
int mf2_0 = InnerClass.this.myField;
int mf2_1 = InnerClassExample.InnerClass.this.myField; // лапша? Але незвично.
int mf3_0 = InnerInnerClass.this.myField;
int mf4_1 = InnerClassExample.InnerClass.InnerInnerClass.this.myField; // лапша? Але незвично.
}
}
}
}
public class InnerClassCreate {
public static void main(String[] args) {
// 1. Помітили, що аналізатор коду на хабре, другий і третій new не присвітив
InnerInnerClass one = new InnerClassExample().new InnerClass().new InnerInnerClass();
// 2
InnerClass innerClass = new InnerClassExample().new InnerClass();
InnerInnerClass two = innerClass.new InnerInnerClass();
// 3
InnerInnerClass three = getInnerClass().new InnerInnerClass();
}

private static final InnerClass getInnerClass() {
return new InnerClassExample().new InnerClass();
}
}

З прикладами коду мабуть і все.

Статистика по класах
Я зібрав статистику за деякими типами і видами класів rt.jar з jdk1.7.0_60 під Mac Os. І такі дані
Опис класу
Локальні 21
Анотації 137
Перерахування 278
Внутрішні (не статичні) 1482
Абстрактні 1560
Анонімні 2230
Інтерфейси 2352
Вкладені статичні 3222
Звичайні 12943
Всього в мене під аналіз потрапило 19898 класів. Дякую за увагу та приємного Вам часу проведення часу.
Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.