JavaRush /جاوا بلاگ /Random-SD /10 شيون جيڪي توهان جاوا بابت نه ڄاڻندا هئا
minuteman
سطح

10 شيون جيڪي توهان جاوا بابت نه ڄاڻندا هئا

گروپ ۾ شايع ٿيل
سو، ڇا توھان تازو ئي جاوا سان ڪم ڪرڻ شروع ڪيو آھي؟ اهي ڏينهن ياد ڪريو جڏهن ان کي "Oak" سڏيو ويندو هو، جڏهن اعتراض-اورينٽيشن اڃا تائين هڪ گرم موضوع هو، جڏهن C++ ماڻهن جو خيال هو ته جاوا جو ڪو به موقعو ناهي، ۽ جڏهن ڪنهن به ايپليٽ جي باري ۾ نه ٻڌو هو؟ مان سمجهان ٿو ته توهان کي هيٺين شين مان اڌ به خبر ناهي. اچو ته هفتي جي شروعات ڪريون جاوا جي اندروني ڪم جي باري ۾ ڪجهه ٿڌي تعجب سان. 10 شيون جيڪي توهان جاوا بابت نه ڄاڻندا هئا - 11. جانچيل استثناءَ جي ڪا به شيءِ ناهي. اهو درست آهي! JVM کي اهڙي ڪا به خبر ناهي، صرف جاوا ٻولي ڪري ٿي. اڄ هرڪو اتفاق ڪري ٿو ته چڪاس ٿيل استثنا هڪ ​​غلطي هئي. جيئن بروس ايڪيل پنهنجي آخري ڳالهه ٻولهه ۾ GeeCON ۾ پراگ ۾ چيو، ٻي ڪا به ٻولي نه آهي جتان جاوا چيڪ ٿيل استثنا استعمال ڪري ٿو، ايستائين جاوا 8 هاڻي انهن کي نئين اسٽريمز API ۾ شامل نه ڪندو آهي (جيڪي ٿوري تڪليف ٿي سگهي ٿي جڏهن توهان جو ليمبڊاس IO يا JDBC استعمال ڪن ٿا). ڇا توھان ثبوت چاھيو ٿا ته JVM اھڙي شيء کي نه ڄاڻندو آھي؟ ھيٺ ڏنل ڪوڊ جي ڪوشش ڪريو: نه رڳو اھو گڏ ڪندو، اھو پڻ ھڪڙو SQLException اڇليندو، توھان کي ان لاءِ Lombok's @SneakyThrows استعمال ڪرڻ جي به ضرورت نه آھي. 2. توھان وٽ اوورلوڊ ٿيل طريقا آھن جيڪي صرف واپسي جي قسمن ۾ مختلف آھنpublic class Test { // No throws clause here public static void main(String[] args) { doThrow(new SQLException()); } static void doThrow(Exception e) { Test. doThrow0(e); } @SuppressWarnings("unchecked") static void doThrow0(Exception e) throws E { throw (E) e; } } Это не откомпorруется, верно? class Test { Object x() { return "abc"; } String x() { return "123"; } } Верно. Язык Java не позволяет одновременно двум методам быть эквивалентно переопределенными в пределах одного класса, не обращая внимания на их отличая в throws либо return типах. Но подождите minutesку. Проверьте еще раз documentацию по Class.getMethod(String, Class…). Там написано: Отметьте, что, возможно, есть более одного соответствующего метода в классе, потому что, пока язык Java запрещает множество методов с одинаковой сигнатурой но разными возвращаемыми типами, виртуальная машина Java этого не делает. Эта гибкость в виртуальной машине может использоваться для реализации различных функций языка. Например, ковариантные возвраты могут осуществляться с bridge методами; bridge метод и переопределенный метод, имели бы одинаковую сигнатуру но разные возвращаемые типы. Ничего себе, да это имеет смысл. На самом деле это довольно много что происходит, когда вы пишете следующее: abstract class Parent { abstract T x(); } class Child extends Parent { @Override String x() { return "abc"; } } Посмотрите на сгенерированный byte code: // Method descriptor #15 ()Ljava/lang/String; // Stack: 1, Locals: 1 java.lang.String x(); 0 ldc [16] 2 areturn Line numbers: [pc: 0, line: 7] Local variable table: [pc: 0, pc: 3] local: this index: 0 type: Child // Method descriptor #18 ()Ljava/lang/Object; // Stack: 1, Locals: 1 bridge synthetic java.lang.Object x(); 0 aload_0 [this] 1 invokevirtual Child.x() : java.lang.String [19] 4 areturn Line numbers: [pc: 0, line: 1] Итак, t на самом деле an object в byte codeе. Это хорошо понимается. Синтетический bridge метод на самом деле генерируется компилятором потому что тип возвращаемого значения Parent.x() можно ожидать на определенных участках вызовов. Добавление generics без таких bridge методов перестанет быть возможным в двоичном представлении. Итак, изменения в JVM чтобы позволить такую функцию произвело меньше боли (которая также позволяет ковариантное переопределение метода в качестве побочного эффекта…) По умному правда? 3. Все следующее – двумерные массивы. class Test { int[][] a() { return new int[0][]; } int[] b() [] { return new int[0][]; } int c() [][] { return new int[0][]; } } Это на самом деле так. Даже если ваш умственный анализатор, не может сразу понять возвращаемый тип из описанных выше способов, все они одинаковы! Как и следующий кусок codeа. class Test { int[][] a = {{}}; int[] b[] = {{}}; int c[][] = {{}}; } Вы думаете, что это безумие? Количество возможностей написать тоже просто взрывает воображение! @Target(ElementType.TYPE_USE) @interface Crazy {} class Test { @Crazy int[][] a1 = {{}}; int @Crazy [][] a2 = {{}}; int[] @Crazy [] a3 = {{}}; @Crazy int[] b1[] = {{}}; int @Crazy [] b2[] = {{}}; int[] b3 @Crazy [] = {{}}; @Crazy int c1[][] = {{}}; int c2 @Crazy [][] = {{}}; int c3[] @Crazy [] = {{}}; } Type annotation. Устройство загадочность которого уступает только его мощи. Или другими словами: Когда я делаю последний коммит How раз перед моим 4-х недельным отпуском. 10 شيون جيڪي توهان جاوا بابت نه ڄاڻندا هئا - 2 Я разрешаю вам пользоваться любым понравившимся вам способом. 4. Вы не получите условное выражение Итак, вы думали, что уже знаете все про условные выражения, когда начали их использовать? Позвольте вас огорчить – вы ошибались. Большинство из вас подумает что следующие два примера эквивалентны: Object o1 = true ? new Integer(1) : new Double(2.0); эквивалентно этому? Object o2; if (true) o2 = new Integer(1); else o2 = new Double(2.0); Нет. Давайте используем быстрый тест System.out.println(o1); System.out.println(o2); Программа выведет следующее: 1.0 1 Да! Условный оператор будет осуществлять приведение типов, если понадобится. Поскольку в ином случае вы ожидали бы что программа бросит NullPointerException? Integer i = new Integer(1); if (i.equals(1)) i = null; Double d = new Double(2.0); Object o = true ? i : d; // NullPointerException! System.out.println(o); 5. Вы также не получите составной оператор назначения. Изворотливости достаточно? Давайте рассмотрим следующие два фрагмента codeа: i += j; i = i + j; Интуитивно, они должны быть равняться правда? Но знаете что – они разные. Спецификация JLS говорит: Составное выражение типа Е1 ор = Е2 эквивалентно Е1 = (Т) ((Е1) ор (Е2)), где Т это тип Е1, за исключение что Е1 вычисляется только один раз. Хороший пример это использовать *= or /= : byte b = 10; b *= 5.7; System.out.println(b); // prints 57 or: byte b = 100; b /= 2.5; System.out.println(b); // prints 40 or: char ch = '0'; ch *= 1.1; System.out.println(ch); // prints '4' or: char ch = 'A'; ch *= 1.5; System.out.println(ch); // prints 'a' Итак, это до сих полезный инструмент? 6. Случайные целочисленные числа Теперь более трудное задание. Не читайте решение. Посмотрите сможете ли вы найти ответ самостоятельно. Когда я запущу следующую программу: for (int i = 0; i < 10; i++) { System.out.println((Integer) i); } иногда я получаю следующий вывод: 92 221 45 48 236 183 39 193 33 84 Но How такое вообще возможно? Ок, ответ в кроется в переопределении JDK кеша Integer через рефлексию, и затем в использовании auto-boxing и auto-unboxing. Не делайте этого без разрешения взрослых! Или другими словами: 10 شيون جيڪي توهان جاوا بابت نه ڄاڻندا هئا - 3 7. GOTO Одно из моих самых любимых. У Java есть GOTO! Напишите это: int goto = 1; и вы получите это: Test.java:44: error: expected int goto = 1; ^ Это потому что goto это неиспользуемое зарезервированное слово, просто на всякий случай… Но это не самая захватывающая часть. Самое интересное то что вы можете включить goto в паре с break, continue и помеченных блоков: Прыжки вперед label: { // do stuff if (check) break label; // do more stuff } В byte codeе: 2 iload_1 [check] 3 ifeq 6 // Jumping forward 6 .. Прыжки назад label: do { // do stuff if (check) continue label; // do more stuff break label; } while(true); В byte codeе: 2 iload_1 [check] 3 ifeq 9 6 goto 2 // Jumping backward 9 .. 8. У Java есть псевдонимы типов В других языках (например Ceylon), мы можем определять псевдонимы типов очень легко: interface People => Set ; Класс People здесь построен таким образом, что может взаимозаменяться множеством Set : People? p1 = null; Set ? p2 = p1; People? p3 = p2; В Java мы не можем просто так определить псевдоним на верхнем уровне. Но мы можем сделать так для потребностей класса либо метода. Давайте предположим что нас не устраивают такие имена How Integer, Long и т.д. и мы хотим имена по короче: I и L. Да легко: class Test { void x(I i, L l) { System.out.println( i.intValue() + ", " + l.longValue() ); } } В примере выше, Integer преобразован в I для видимости класса Test в то время How Long преобразован в L для нужд метода х(). Теперь мы можем вызвать этот метод следующим образом: new Test().x(1, 2L); Конечно эту технику не следует воспринимать всерьез. В данном случае Integer и Long final типы, что означает что I и L – эффективные преобразования (почти, преобразование идет только в одну сторону). Если бы мы решor использовать non-final типы (к примеру Object), тогда мы могли бы обойтись обычными дженериками. Поигрались немного и хватит. Давай перейдем к чему-то по настоящему интересному. 9. Некоторые отношения типов неразрешимы! Хорошо, сейчас будет действительно интересно, так что возьмите чашку концентрированного кофе и давайте рассмотрим следующие два типа: // A helper type. You could also just use List interface Type {} class C implements Type > {} class D

implements Type >>> {} پوء سي ۽ ڊي جو مطلب ڇا آهي؟ هڪ لحاظ کان اهي recursive آهن، java.lang.Enum ۾ ريٽرن وانگر. غور ڪريو: مٿي ڏنل وضاحتن کي، اينيم جو اصل عمل صرف نحوي کنڊ آهي: انهي کي ذهن ۾ رکندي، اچو ته اسان جي ٻن قسمن ڏانهن موٽون. ھيٺ ڏنل ڪوڊ گڏ ڪندو؟ هڪ ڏکيو سوال ... ۽ اهو اصل ۾ حل نه ٿي رهيو آهي؟ ڇا سي هڪ ذيلي قسم جو قسم آهي ؟ ان کي گڏ ڪرڻ جي ڪوشش ڪريو پنھنجي Eclipse يا Idea ۾ ۽ اھي اوھان کي ٻڌائيندا ته اھي ڇا ٿا سوچين. ان کي فلش ڪريو ڊرين هيٺ... جاوا ۾ ڪجهه قسم جا رشتا ناقابل فيصلا هوندا آهن! 10. ٽائپ انٽرسيڪشن جاوا ۾ ھڪ تمام دلچسپ خصوصيت آھي جنھن کي ٽائپ انٽرسيڪشن چئجي ٿو. توھان اعلان ڪري سگھو ٿا ھڪڙو (عام) قسم جيڪو اصل ۾ ٻن قسمن جو چونڪ آھي. مثال طور: ڪسٽم ٽائيپ پيراميٽر T جنهن کي توهان ٽيسٽ ڪلاس جي مثالن سان ڳنڍيندا آهيو ان ۾ لازمي طور تي سيريلائيبل ۽ ڪلون ايبل انٽرفيس شامل آهن. مثال طور، اسٽرنگ کي محدود نه ٿو ڪري سگھجي، پر تاريخ ڪري سگھي ٿي: ھن خصوصيت جا Java8 ۾ گھڻا استعمال آھن، جتي توھان قسمون کاسٽ ڪري سگھو ٿا. هي ڪيئن مدد ڪري ٿو؟ لڳ ڀڳ ڪجھ به نه، پر جيڪڏھن توھان چاھيو ٿا پنھنجي لامبڊا جو اظهار انھيءَ قسم ۾ جنھن کي توھان جي ضرورت آھي، پوءِ ٻيو ڪو رستو نه آھي. اچو ته چئو ته توهان وٽ توهان جي طريقي ۾ اهڙي چريو حد آهي: توهان چاهيو ٿا Runnable جيڪو هڪ ئي وقت ۾ Serializable آهي صرف ان صورت ۾ جيڪڏهن توهان ان کي ڪنهن ٻئي هنڌ تي عمل ڪرڻ چاهيو ٿا ۽ نتيجو نيٽ ورڪ تي موڪليو. Lambda ۽ سيريلائيزيشن کي ٿورڙي بيزاري شامل ڪري ٿي. توهان پنهنجي لامبڊا اظهار کي سيريل ڪري سگهو ٿا جيڪڏهن ان جو ٽارگيٽ قسم ۽ دليل سيريلائيبل هوندا. پر جيتوڻيڪ اهو سچ آهي، اهي خودڪار طور تي Serializable انٽرفيس کي فعال نه ڪندا آهن. توهان کي انهن کي پاڻ ڏانهن آڻڻ گهرجي. پر جڏهن توهان صرف سيريلائيبل تي اڇلائي سگهو ٿا: ته پوءِ لامبڊا هاڻي هلڻ لائق نه هوندو، تنهن ڪري انهن کي ٻنهي قسمن ۾ اڇلايو: ۽ نتيجي ۾: public abstract class Enum > { ... } // This enum MyEnum {} // Is really just sugar for this class MyEnum extends Enum { ... } class Test { Type c = new C(); Type> d = new D (); } Step 0) C Step 1) Type > >? Step 0) D > Step 1) Type >>> > Step 2) D >> Step 3) List >> > Step 4) D > >> Step . . . (expand forever) class Test { } // Doesn't compile Test s = null; // Compiles Test d = null; void execute(T t) {} execute((Serializable) (() -> {}));execute((Runnable & Serializable) (() -> {}));

جاوا ايترو طاقتور آهي جيترو پراسرار آهي.

تبصرا
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION