Comments 56
Мсье знает толк в…
0
Столько слов, а суть одна: reference type != object type :)
+6
Есть предположения — почему это не компилируется?
public class HelloWorld{
public static void main(String []args){
Class<HelloWorld> aClass = ((HelloWorld)null).class;
System.out.println(aClass);
}
}
(попробовал здесь www.compileonline.com/compile_java_online.php)
p.s. прощу прощения, но не тег source не помогает
public class HelloWorld{
public static void main(String []args){
Class<HelloWorld> aClass = ((HelloWorld)null).class;
System.out.println(aClass);
}
}
(попробовал здесь www.compileonline.com/compile_java_online.php)
p.s. прощу прощения, но не тег source не помогает
+2
Недавно столкнулся с такой штукой, раньше не знал.
Объявляем
И получаем, что
Объявляем
A obj = new A()
{
@Override
public void someOverridenMethod()
{
...
}
};
И получаем, что
obj.getClass()
нам вообще класс замыкания вернёт. И тогда надо дёргать getEnclosingClass()+2
Пару лет назад очень увлекались в проекте анонимными классами. А потом огребли=)
+1
Замыкание (closure) и вложение (enclosure) — разные же понятия, не?
+1
С enum еще интересней:
enum E {
first,
second {
public String toString() {
return "not first";
}
}
}
E.first.getClass().isEnum()
вернет true;E.second.getClass().isEnum()
вернет false!+1
Чуть выше уже говорили про getEnclosingClass() :)
0
И чего? :) Как нам это поможет отличить объект вложенного класса от enum константы?
Я хотел сказать, что проверять, является ли объект enum константой, через
А правильно будет
enum E {
first,
second {
public String toString() {
return "not first";
}
};
static class Helper { }
public static final Helper helper = new Helper();
}
E.second.getClass().getEnclosingClass().isEnum()
вернет true;E.helper.getClass().getEnclosingClass().isEnum()
вернет true.Я хотел сказать, что проверять, является ли объект enum константой, через
obj.getClass().isEnum()
— неправильно.А правильно будет
obj instanceof Enum
.+3
Красота, не знаю, куда впихнуть в пост, но примерчик возьму на заметку, спасибо;)
+1
a.class;
Как у вас вообще это скомпилировалось?
UPD: Снято. Ответили выше.
0
Автор, после достаточно интересного предыдущего поста пост про
ИМХО, такое максимум тянет на мини-шпаргалку для осваивающих Java.
.getClass()
— это даже не шаг назад, а какой-то прыжок со скалы вниз.ИМХО, такое максимум тянет на мини-шпаргалку для осваивающих Java.
0
Вообще, про getClass я узнал ровно два года назад, т.ч. возможно это действительно прыжок со скалы;) Тем не менее многие мои знакомые удивлялись, что сигнатура в Object.java != реально проверяемая компилятором сигнатура. Возможно, привычка стараться все объяснять максимально подробно и угробила «короткий забавный факт», превратив его в пост-мини-шпаргалку-для-новичков. Буду учиться на ошибках, спасибо.
+1
Никакого знака неравенства между сигнатурами там нет. Object — это корневой элемент в любой классовой иерархии, поэтому extends Object в сигнатуре можно не писать. Вы же не пишете в каждом вашем классе, что он extend Object, верно?
-1
Как то, что любой класс по дефолту наследуется от Object связано с тем, что в этом самом Object написано
Class<?> getClass()
, а на самом деле там не совсем такой дженерик?+1
pastebin.com/nB8THEMy
Это равнозначные записи. После компиляции это будет одно и то-же. Первый и второй вариант — сокращенные формы записи третьего варианта, синтаксический сахар, как и то, что «любой класс по дефолту наследуется от Object» (вас не заставляют каждый раз явно прописывать extend Object). Не понимаю вашего недоумения.
Это равнозначные записи. После компиляции это будет одно и то-же. Первый и второй вариант — сокращенные формы записи третьего варианта, синтаксический сахар, как и то, что «любой класс по дефолту наследуется от Object» (вас не заставляют каждый раз явно прописывать extend Object). Не понимаю вашего недоумения.
-1
Ещё раз могу повторить. Об «extends Object» речь не идёт вообще, а идёт о том, что если бы компилятор проверял сигнатуру, указанную в Object, то нельзя было бы написать
Class<String> cls = String.class
.+1
Как уже сказали, String.class это не поле. Оно возвращает ровно то что должно — дженерик с конкретным типом. Причем тут вообще сигнатура Object#getClass().
-1
Да, извиняюсь. Нельзя было бы написать
Class<? extends String> cls = "".getClass();
(а на самом деле можно).+1
Почему нельзя? Все снова сводится к моему комментарию, на который Вы ответили #6859374. Там именно такая сигнатура, которая должна быть, иначе бы #getClass() нельзя было заоверрайдить при наследовании. И компилятор проверяет именно такую сигнатуру, потому что String к Object апкастится отлично. Знака неравенства между сигнатурами нет.
Я все еще не понимаю, что здесь неочевидного.
Я все еще не понимаю, что здесь неочевидного.
-1
иначе бы #getClass() нельзя было заоверрайдить при наследовании
Так и нельзя, он final.
И компилятор проверяет именно такую сигнатуру, потому что String к Object апкастится отлично.
Ну вот попробуйте определить свой метод, который возвращает
Class<?>
, напримерpublic class Foo {
public Class<?> getClass1() { return getClass(); }
}
Тогда
Foo foo = new Foo();
Class<? extends Foo> cls1 = foo.getClass();
Class<? extends Foo> cls2 = foo.getClass1();
cls1 компилируется, а cls2 нет. Притом, что сигнатура getClass() (в Object.java) и getClass1() выглядит одинаково.
+1
> Так и нельзя, он final.
А еще native, и особой магией все таки оверрайдится, что бы возвращать то, что возвращает. В вашем варианте сигнатуры это было бы невозможно.
> cls1 компилируется, а cls2 нет. Притом, что сигнатура getClass() (в Object.java) и getClass1() выглядит одинаково.
Если добавить final, разве не скомпилируется? Тут надо сесть и более детально разобраться, сдается мне сигнатура cls2 только на первый взгляд выглядит одинаково.
А еще native, и особой магией все таки оверрайдится, что бы возвращать то, что возвращает. В вашем варианте сигнатуры это было бы невозможно.
> cls1 компилируется, а cls2 нет. Притом, что сигнатура getClass() (в Object.java) и getClass1() выглядит одинаково.
Если добавить final, разве не скомпилируется? Тут надо сесть и более детально разобраться, сдается мне сигнатура cls2 только на первый взгляд выглядит одинаково.
-1
> Class result1 = a.getClass(); // Compilation error!
Ну так разумеется же
Class
Ну так разумеется же
Class
-1
> Так что в Object.java написана одна сигнатура, а компилятор подставляет другую.
Чего это вдруг?
Сигнатура та же. Просто в джавадоке более детально указано что может быть возвращено, чем в дженерике в сигнатуре.
Так может каждый
Чего это вдруг?
Сигнатура та же. Просто в джавадоке более детально указано что может быть возвращено, чем в дженерике в сигнатуре.
Так может каждый
/** * return The actual result type is String. */ public static <?> getSomething() { return "Hello World"; }
0
P.S. А, понял о чем речь.
> на вопрос, почему не компилировался пример выше
Вопрос не в том был почему не компилируется
Здесь да, должен был быть cast, но он не нужен. Действительно, есть какая-то хитрость.
> на вопрос, почему не компилировался пример выше
Вопрос не в том был почему не компилируется
Class<A> result1 = a.getClass();
, а в том почему компилируется Class<? extends A> result = a.getClass();
Здесь да, должен был быть cast, но он не нужен. Действительно, есть какая-то хитрость.
0
Что значит «не нужен». Обычный неявный каст, как здесь:
class Object1 { }
class Object2 extends Object1 { }
Object2 obj2 = new Object2();
Object1 obj1 = obj2;
class Object1 { }
class Object2 extends Object1 { }
Object2 obj2 = new Object2();
Object1 obj1 = obj2;
0
Ну это просто upcast, приведение к классу-предку. Это совсем не то, и дженериков нет — что какбы намекает.
0
На этот вопрос как мне кажется Эккель отвечает в «Thinking in Java 4 edition» (к великому сожалению читал в переводе).
1. В главе про RTTI говорится что:
Значит код:
содержит ошибку — объектная ссылка a может ссылаться (пардон за каламбур) на объект класса-потомка A, что недопустимо. Соответственно, расширение параметра типа проблему решает:
2. А в главе про параметризацию дается объяснение (как мне кажется) всего этого мракобесия:
А это значит что код:
выведет:
что говорит о том, что при использовании параметризации вся конкретная информация о типе утрачивается :( и по словам Эккеля это есть проблема Java. Автор указал на это:
но сидящие здесь гуру (никакого сарказма!), намекают на это настолько неявно и как само собой разумеющееся, что я занервничал и решил-таки побыть К.О. и попозориться немного
1. В главе про RTTI говорится что:
Если обычная ссылка на класс может быть связана с любым объектом Class, параметризованная ссылка может связываться только с объектами типа, указанного при объявлении.
Значит код:
Class<A> result1 = a.getClass(); //error
содержит ошибку — объектная ссылка a может ссылаться (пардон за каламбур) на объект класса-потомка A, что недопустимо. Соответственно, расширение параметра типа проблему решает:
Class<? extends A> result = a.getClass();//ok
2. А в главе про параметризацию дается объяснение (как мне кажется) всего этого мракобесия:
Параметризация в Java реализуется с применением стирания (erasure).
А это значит что код:
class A{}
class B{}
List<A> aList = new ArrayList<A>();
List<B> bList = new ArrayList<B>();
System.out.println(aList.getClass().equals(bList.getClass() ) );
выведет:
true
что говорит о том, что при использовании параметризации вся конкретная информация о типе утрачивается :( и по словам Эккеля это есть проблема Java. Автор указал на это:
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.
но сидящие здесь гуру (никакого сарказма!), намекают на это настолько неявно и как само собой разумеющееся, что я занервничал и решил-таки побыть К.О. и попозориться немного
0
> при использовании параметризации вся конкретная информация о типе утрачивается
… в рантайме.
Но речь шла о compilation error.
… в рантайме.
Но речь шла о compilation error.
0
Подтвержаю насчет runtime. Далее надеюсь не спороть чепухи. Насколько я понял в Java параметризация реализуется с применением стирания и это учитывается/влияет на компилятор. А вот результат стирания можно увидеть в runtime. И да, мой код совсем не к месту. К месту другой:
Из-за стирания параметра типа код внутри дженерика не знает о наличии метода someFn(). Могу предположить что подобная мантра:
class SomeObject{
public void someMeth(){
System.out.println("Hello");
}
}
class Controller<T>{
private T mObj = null;
public Controller(T obj){
mObj = obj;
}
public void control(){
mObj.someMeth();// <---- compilation error!
}
}
public class Parameterising {
public static void main(String [] args){
SomeObject someObj = new SomeObject();
Controller<SomeObject> controller = new Controller<SomeObject>(someObj);
controller.control();
}
}
Из-за стирания параметра типа код внутри дженерика не знает о наличии метода someFn(). Могу предположить что подобная мантра:
Информация о параметрах типов недоступна внутри параметризованного кодаотрабатывает и в методе Object.getClass(). И только ограничения параметра типа
class Controller<T extends SomeObject>{...}
могут спасти ситуацию 0
Почему это надо спасать ситуацию. Я не представляю, как можно знать о типе , не указав от чего он наследуется. Получается какая-то динамика, сложно представить что должна проделать IDE, чтобы работало автодополнение для метода someMeth.
0
Все-таки это не имеет отношения к стиранию типа (хоть я и солидарен с Эккелем=))
Пример не скомпилируется не только в джаве, но и в любом другом языке. Дженерик — это обобщение (см. словарь) — это по определению «нечто, работающее с 'произвольным' типом». И крутизна как раз в том, что произвольный тип — это не обязательно Object.
Грубо говоря, вместо дженериков можно работать с Object'ами (в рантайме так и есть), но тогда компилятор и не будет проводить дополнительных проверок.
В вашем примере someObj — с точки зрения Controller \<T\> — объект некоего, заранее неизвестного, класса T. Причем Т — произвольно, никаких ограничений.
То, что Вы неявно просите — это чтобы каждое использование дженерика — в данном случае Controller\<SomeObject\> — накладывало ограничения на дженерик.
В частности, кто-то другой может написать
Вот откапитанил, так откапитанил;)
Пример не скомпилируется не только в джаве, но и в любом другом языке. Дженерик — это обобщение (см. словарь) — это по определению «нечто, работающее с 'произвольным' типом». И крутизна как раз в том, что произвольный тип — это не обязательно Object.
Грубо говоря, вместо дженериков можно работать с Object'ами (в рантайме так и есть), но тогда компилятор и не будет проводить дополнительных проверок.
В вашем примере someObj — с точки зрения Controller \<T\> — объект некоего, заранее неизвестного, класса T. Причем Т — произвольно, никаких ограничений.
То, что Вы неявно просите — это чтобы каждое использование дженерика — в данном случае Controller\<SomeObject\> — накладывало ограничения на дженерик.
В частности, кто-то другой может написать
Controller<Object> controller = new Controller<Object>(new Object());
Вот откапитанил, так откапитанил;)
0
подобный код (с неизвестным типом) легко скомпилируется в с++, например. просто потому что реальный код класса появится только во время явного указания типа (то есть специализации), а пока класса нет, дженерик — просто кусок исходника.
в яве, из-за того что нет технической возможности иметь отдельно объявление и реализацию — получились вот такие, какие есть, дженерики
в яве, из-за того что нет технической возможности иметь отдельно объявление и реализацию — получились вот такие, какие есть, дженерики
0
Да, Вы правы.
0
В C# реализованы Дженерики как надо, там такое будет работать (вроде нет)? В С++ шаблоны, которые довольно сложны в анализе для компиляторов. Ведь это нарушение правила — интерфейс должен быть известен в момент написания кода, в данном случае интерфейс становится известен только когда мы объявим объект. Выглядит это как костыль.
0
Не как костыль, а как template. Что собственно и было сделано. Дженерик — это просто удобное следствие.
В с# есть слово where, которое и помогает ему узнать какие методы есть у типа-параметра. Так что как в с++ — c# тоже не умеет
В с# есть слово where, которое и помогает ему узнать какие методы есть у типа-параметра. Так что как в с++ — c# тоже не умеет
0
Вот этот самый where и есть тот самый интерфейс. В Java можно создать интерфейс и поместить в extends. Интерфейс это лишь описание набора методов без реализации.
Не нужно брать С++ за эталон, это не простой язык.
Не нужно брать С++ за эталон, это не простой язык.
0
UFO just landed and posted this here
В который раз поражаюсь, как много можно придумать вопросов для собеседования, которые абсолютно никак не показывают навык программиста.
+11
А много вы знаете вопросов, которые показывают навык программиста?
Понять, на что способен человек можно
а) по коду, который он пишет
б) по разговору
И вот этот конкретный случай лично я считаю прекрасным поводом для разговора на джуниора-мидла
Ex.:
1) поговорили про дженерики и коллекции
2) А какие вы знаете еще примеры дженериков? Если отвечает — прекрасно
3) если задумался — подкидываем удочку: а как насчет Class? Если уверенно отвечает, то фиг с ним, не судьба
4) Если удивился — стимулируем подумать: «А как Вы думаете, зачем?» Намекаем на возвращаемый аргумент метода, просим написать метод, возвращающий произвольный тип
5) Ну и наконец — а что по-вашему должен возвращать getClass? А как это написать? Ну и говорим, что это особенный метод
Я вообще считаю, что добиться на собеседовании, чтобы кандидат рассуждал на тему того, чего не знает — это как раз и есть один из способов оценить навык программиста и опыт. Конкретные вопросы — это скучно. Они быстро устраревают и отлично гуглятся (хотя я бы не сказал, что умение подготовиться — это плохо;), но это другая история)
Понять, на что способен человек можно
а) по коду, который он пишет
б) по разговору
И вот этот конкретный случай лично я считаю прекрасным поводом для разговора на джуниора-мидла
Ex.:
1) поговорили про дженерики и коллекции
2) А какие вы знаете еще примеры дженериков? Если отвечает — прекрасно
3) если задумался — подкидываем удочку: а как насчет Class? Если уверенно отвечает, то фиг с ним, не судьба
4) Если удивился — стимулируем подумать: «А как Вы думаете, зачем?» Намекаем на возвращаемый аргумент метода, просим написать метод, возвращающий произвольный тип
5) Ну и наконец — а что по-вашему должен возвращать getClass? А как это написать? Ну и говорим, что это особенный метод
Я вообще считаю, что добиться на собеседовании, чтобы кандидат рассуждал на тему того, чего не знает — это как раз и есть один из способов оценить навык программиста и опыт. Конкретные вопросы — это скучно. Они быстро устраревают и отлично гуглятся (хотя я бы не сказал, что умение подготовиться — это плохо;), но это другая история)
+1
Если спрашивать лично мое мнение, то я считаю, что человек от вопросов, с которыми не сталкивался (а редко кто сталкивается с getClass() в работе, а не а разработке собственных ORM, хаках и пр.), может впасть в панику и в худшем случае наговорить глупости, а в лучшем просто почувствовать себя дерьмом и бестолочью (это, к примеру, после дюжины лет опыта и десятка завершенных больших проектов). Конечно, без каверзных вопросов сложно понять уровень и адекватность, но ответы на них могут и быть просто заучены. Думаю, хоть сколько-либо достоверно уровень показать может лишь тестовое задание, или даже испытательный срок.
Но я еще раз повторюсь — это только мое мнение, причем, я очень наивен в этом вопросе, а когда пару раз нанимал программистов, в основном изучал их предыдущие проекты и просил рассказать, какую они роль там выполняли, с какими проблемами сталкивались. «Пытки» же оставлял другим собеседователям :)
Но я еще раз повторюсь — это только мое мнение, причем, я очень наивен в этом вопросе, а когда пару раз нанимал программистов, в основном изучал их предыдущие проекты и просил рассказать, какую они роль там выполняли, с какими проблемами сталкивались. «Пытки» же оставлял другим собеседователям :)
+2
На мой взгляд реализация .class и .getClass в Java вполне логична.
+2
Возращаемый тип не входит в сигнатуру метода, там только название метода и передаваемые параметры.
+1
Да, сам влетал в это. Прямое следствие негибких явовских генериков. Вот и пришлось такой костыль прикручивать. Таже абсолютно тема, что и якобы нативные методы в JDK, которые просто на уровне JVM подменяются на платформозависимые реализации, а никакого JNI там нет.
0
Sign up to leave a comment.
А знаете ли Вы, что возвращает .getClass()?