Объясните, пожалуйста, почему в строчке //(?!) не выбрасывается исключение ClassCastExeption? Там стоит (T), где T=String, а значит там (String) new Integer(42), что должно давать исключение... JVM должна привести тип, но почему-то она этого не делает. Дальше мы смотрим тип t и видим Integer, что действительно является реальным типом t. Но факт того, что мы вывели тип, показывает: исключение не создалось. Потом программа выбрасывает исключение там, где была вызвана функция A.
Кстати! Помнится мне, Bruce Eckel учит тому, что существует некоторый декомпилятор javap, он сам пишет код программы то ли из байт-кода, то ли из .class... Не изучал. Если кто сведущ в этом, попробуйте заставить декомпилятор построить этот кусок кода, может он покажет, что там происходит на самом деле, и почему?
public class A {
public static
public static void main(String[] args) {
System.out.println(A.
Новое:
Итак, друзья. Самым частым ответом стало: метод f() -- это обычный метод, который компильнулся как обычный, но с учётом того, что результаты его вызовов приводятся к типу, передаваемому как
System.out.println(A.
Удивительно, но в консоли выйдет:
//output:
class java.lang.Integer
42
Так вот если у нас невидимо это выглядит так:
System.out.println((Double)A.
То попробуйте это компильнуть -- выйдет ошибка ахахахха, от куда вывод, что всё таки работает оно не так...
Мои догадки на этот счёт таковы, что метод println() перегружен, и в случае A.
Для меня здесь странно то, что почему-то в первом случае, когда мы вызывали A.
Хотите сказать, что вся технология такая умная, что если возврат параметризованной/обобщённой функции проверяется на существование как аргумент в перегруженном методе println() с успехом, то приведение не вызывается [например String версия println() существует, ага, приведём выходной Object от A.f() к указанному String], а если перегруженной версии с указанным в типе-параметре для A.f() классом версии println() нет, то он (компилятор) оставляет версию println(Object) и решает не пытаться даже сделать (Object)(Double)A.
Итого: Я хочу объяснения принципа работы параметризованных методов (классов), такое объяснение, чтобы был точно понятен алгоритм действий компилятора для создаваемого кода. Если какие-то мои догадки на счёт действий компилятора верны, то я хочу их (моих догадок) подтверждения и очень желательно (!) с ссылкой на какие-нибудь документационные файлы!
Ответ
Это байткод main при вызове A.
public static void main(java.lang.String[]);
Code:
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: invokestatic #7 // Method f:()Ljava/lang/Object;
6: invokevirtual #6 // Method java/io/PrintStream.println:(Ljava/lang/Object;)V
9: return
А это байткод main при вызове A
public static void main(java.lang.String[]);
Code:
0: getstatic #4 // Field java/lang/System.out:Ljava/io/PrintStream;
3: invokestatic #7 // Method f:()Ljava/lang/Object;
6: checkcast #8 // class java/lang/String
9: invokevirtual #9 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
12: return
Обратите внимание на строку invokevirtual, в первом случае вызывается метод с сигнатурой println(Object o), во втором - println(String s)
В случае с Double после стирания типов метод f() возвращает Object, метода println для Double нету (да, можно анбоксить Double и вызвать println для примитива double, но приоритет отдаётся выбору подходящего метода), поэтому выбирается метод для Object, внутри спокойно вызывается String.valueOf и всё хорошо.
В случае со String есть метод println для String и компилятор решает, что нужно вызывать именно его, поэтому там есть каст.
Я обернул System.out.println в свой println для 3 типов:
static void println(Double d){
System.out.println(String.valueOf(d));
}
static void println(String s){
System.out.println(String.valueOf(s));
}
static void println(Object o){
System.out.println(String.valueOf(o));
}
И теперь вы уже догадались что будет при вызове?
public static void main(String[] args) {
println(Main.
Компилятор увидит, что есть перегруженный println для Double и поставит каст (и тут как раз ошибка будет):
public static void main(java.lang.String[]);
Code:
0: invokestatic #7 // Method f:()Ljava/lang/Object;
3: checkcast #8 // class java/lang/Double
6: invokestatic #9 // Method println:(Ljava/lang/Double;)V
9: return
Комментариев нет:
Отправить комментарий