Я думаю, почти любого Java разработчика когда-то спрашивали на собеседовании: «Какие есть методы у класса Object?»
Меня, по крайней мере, спрашивали неоднократно. И, если в первый раз это было неожиданностью (кажется, забыл про clone), то потом я был уверен, что уж методы Object'а-то я знаю;)
И каково же было мое удивление, когда спустя несколько лет разработки я наткнулся на собственное незнание сигнатуры метода getClass()
Под катом пара слов про Class, .class, .getClass и, собственно, сюрприз, на который я наткнулся.
Итак, у нас есть класс А и объект этого класса a:
Начнем с простого. При вызове getClass() может отработать полиморфизм, и результатом будет класс-потомок.
Но это так, цветочки. Идем дальше.
A.class — объект класса Class. Смотрим в Class.java:
Это дженерик. Причем типизирован он, очевидно, этим самым A — классом, у которого вызвали .class
Если подумать, то понятно зачем это нужно: теперь, в частности, можно написать метод, который возвращает произвольный тип, в зависимости от аргумента:
A.class возвращает объект класса Class:
Собрав воедино все вышесказанное, можно догадаться, что:
Действительно, ввиду полиморфизма нужно не забывать, что фактический класс объекта a — не обязательно A — это может быть любой подкласс:
Все эти дженерики — это, конечно, замечательно, но как записать сигнатуру метода getClass синтаксисом java в классе Object?
А никак:
А на вопрос, почему не компилировался пример выше, ответитМаксим Поташев джавадок к методу:
Так что в Object.java написана одна сигнатура, а компилятор подставляет другую.
Меня, по крайней мере, спрашивали неоднократно. И, если в первый раз это было неожиданностью (кажется, забыл про clone), то потом я был уверен, что уж методы Object'а-то я знаю;)
И каково же было мое удивление, когда спустя несколько лет разработки я наткнулся на собственное незнание сигнатуры метода getClass()
Под катом пара слов про Class, .class, .getClass и, собственно, сюрприз, на который я наткнулся.
Итак, у нас есть класс А и объект этого класса a:
public class A {
}
...
A a = new A();
0. A.class vs a.getClass()
Начнем с простого. При вызове getClass() может отработать полиморфизм, и результатом будет класс-потомок.
public class B extends A {
{
...
A a1 = new B();
a1.getClass(); // то же самое, что B.class
Скрытый текст
Тут была ложь, на которую мне указали в комментариях. class — это не статическое поле, коим может показаться (и даже не нативное-псевдо-статическое поле, как думал я), а особая конструкция языка. И, в отличие от статического поля, обратиться к нему через объект нельзя!
a.class; // Compile error! Unknown class: "a"
Но это так, цветочки. Идем дальше.
1. А что такое этот ваш Class?
A.class — объект класса Class. Смотрим в Class.java:
public final class Class<T> implements ...
Это дженерик. Причем типизирован он, очевидно, этим самым A — классом, у которого вызвали .class
Если подумать, то понятно зачем это нужно: теперь, в частности, можно написать метод, который возвращает произвольный тип, в зависимости от аргумента:
public <T> T foo(Class<T> clazz);
A.class возвращает объект класса Class:
Class<A> result = A.class; // Compilation successfull
2. А что же возвращает a.getClass()?
Собрав воедино все вышесказанное, можно догадаться, что:
Class<A> result1 = a.getClass(); // Compilation error!
Действительно, ввиду полиморфизма нужно не забывать, что фактический класс объекта a — не обязательно A — это может быть любой подкласс:
Class<? extends A> result = a.getClass(); // Compilation successfull
3. А что же написано в Object.java?
Все эти дженерики — это, конечно, замечательно, но как записать сигнатуру метода getClass синтаксисом java в классе Object?
А никак:
public final native Class<?> getClass();
А на вопрос, почему не компилировался пример выше, ответит
The actual result type is Class<? extends |X|> where |X| is the erasure of the static type of the expression on which getClass is called.
Так что в Object.java написана одна сигнатура, а компилятор подставляет другую.