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; } }
class Test { Object x() { return "abc"; } String x() { return "123"; } }
Benar. Bahasa Java tidak mengizinkan dua metode untuk ditimpa secara setara dalam kelas yang sama pada waktu yang sama, terlepas dari perbedaan tipe lemparan atau pengembaliannya. Tapi tunggu sebentar. Periksa kembali dokumentasi untuk Class.getMethod(String, Class…). Dikatakan:
Perhatikan bahwa ada kemungkinan bahwa ada lebih dari satu metode yang sesuai dalam satu kelas, karena meskipun bahasa Java melarang beberapa metode dengan tanda tangan yang sama tetapi tipe pengembalian yang berbeda, Java Virtual Machine tidak melarangnya. Fleksibilitas dalam mesin virtual ini dapat digunakan untuk mengimplementasikan berbagai fitur bahasa. Misalnya, pengembalian kovarian dapat dilakukan dengan metode jembatan; Metode jembatan dan metode yang diganti akan memiliki tanda tangan yang sama tetapi tipe pengembaliannya berbeda. Wah, itu masuk akal. Sebenarnya ada cukup banyak hal yang terjadi ketika Anda menulis yang berikut ini: Lihatlah bytecode yang dihasilkan: Jadi t sebenarnya adalah sebuah objek dalam bytecode. Hal ini dipahami dengan baik. Metode jembatan sintetis sebenarnya dihasilkan oleh kompiler karena tipe kembalian Parent.x() dapat diharapkan pada bagian panggilan tertentu. Menambahkan obat generik tanpa metode jembatan seperti itu tidak lagi dapat dilakukan dalam representasi biner. Jadi, mengubah JVM untuk mengizinkan fitur seperti itu menghasilkan lebih sedikit rasa sakit (yang juga memungkinkan penggantian metode kovarian sebagai efek samping...) Cerdas bukan?
3. Berikut ini adalah array dua dimensi. Ini sebenarnya benar. Bahkan jika penganalisis mental Anda tidak dapat segera memahami tipe pengembalian dari metode yang dijelaskan di atas, semuanya tetap sama! Seperti potongan kode berikutnya. Apakah menurut Anda ini gila? Banyaknya kesempatan untuk menulis juga sungguh meledakkan imajinasi!
Ketik anotasi.
Perangkat yang misterinya nomor dua setelah kekuatannya. Atau dengan kata lain: Ketika saya membuat komitmen terakhir saya sebelum liburan 4 minggu saya. Saya memberi Anda izin untuk menggunakannya sesuka Anda.
4. Anda tidak akan mendapatkan kondisional Jadi, Anda pikir Anda sudah mengetahui segalanya tentang kondisional saat Anda mulai menggunakannya? Biarkan saya mengecewakan Anda - Anda salah. Sebagian besar dari Anda akan berpikir bahwa dua contoh berikut ini setara: setara dengan ini? TIDAK. Mari kita gunakan tes cepat. Program ini akan menampilkan keluaran berikut: Ya! Operator kondisional akan melakukan cast tipe jika diperlukan. Karena jika tidak, Anda akan mengharapkan program tersebut mengeluarkan NullPointerException?
5. Anda juga tidak akan mendapatkan operator penugasan gabungan. Apakah kecerdikan cukup? Mari kita lihat dua cuplikan kode berikut:
abstract class Parent
{ abstract T x(); } class Child extends Parent
{ @Override String x() { return "abc"; } }
// 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]
class Test { int[][] a() { return new int[0][]; } int[] b() [] { return new int[0][]; } int c() [][] { return new int[0][]; } }
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 [] = {{}}; }
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
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);
i += j; i = i + j;
Secara intuitif, keduanya seharusnya setara, bukan? Tapi tahukah Anda - mereka berbeda. Spesifikasi JLS mengatakan:
Ekspresi gabungan tipe E1 op = E2 setara dengan E1 = (T) ((E1) op (E2)), di mana T adalah tipe E1, kecuali E1 dievaluasi hanya sekali. Contoh yang baik adalah dengan menggunakan *= 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'
Jadi, apakah ini masih merupakan alat yang berguna?
6. Bilangan bulat acak Sekarang untuk tugas yang lebih sulit. Jangan membaca solusinya. Lihat apakah Anda dapat menemukan jawabannya sendiri. Ketika saya menjalankan program berikut:
for (int i = 0; i < 10; i++) { System.out.println((Integer) i); }
Kadang-kadang saya mendapatkan output berikut:
92 221 45 48 236 183 39 193 33 84
Tapi bagaimana ini mungkin? Oke, jawabannya terletak pada mengganti cache Integer JDK melalui refleksi, lalu menggunakan auto-boxing dan auto-unboxing. Jangan lakukan ini tanpa izin orang dewasa! Atau dengan kata lain:
7. GOTO Salah satu favoritku. Java memiliki GOTO! Tulis ini:
int goto = 1;
dan Anda mendapatkan ini: Itu karena goto adalah kata khusus yang tidak digunakan, untuk berjaga-jaga... Tapi itu bukan bagian yang menarik. Yang keren adalah Anda dapat memasukkan goto berpasangan dengan break, continue dan blok yang ditandai: Melompat maju Dalam kode byte: Melompat mundur Dalam kode byte:
8. Java memiliki alias tipe Dalam bahasa lain (seperti Ceylon), kita dapat mendefinisikan tipe alias sangat mudah: Kelas People di sini dibuat sedemikian rupa sehingga dapat dipertukarkan dengan Set
Test.java:44: error:
expected int goto = 1; ^
label: { // do stuff if (check) break label; // do more stuff }
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);
2 iload_1 [check] 3 ifeq 9 6 goto 2 // Jumping backward 9 ..
interface People => Set
;
People? p1 = null; Set
? p2 = p1; People? p3 = p2;
class Test {
void x(I i, L l) { System.out.println( i.intValue() + ", " + l.longValue() ); } }
new Test().x(1, 2L);
// A helper type. You could also just use List interface Type
{} class C implements Type
> {} class D
implements Type
Jadi apa maksudnya C dan D? Dalam arti tertentu, mereka bersifat rekursif, mirip dengan rekursi di java.lang.Enum. Pertimbangkan: Mengingat spesifikasi di atas, implementasi sebenarnya dari enum hanyalah gula sintaksis: Dengan mengingat hal tersebut, mari kembali ke dua tipe kita. Apakah kode berikut akan dikompilasi? Sebuah pertanyaan yang sulit... dan sebenarnya belum terselesaikan? Apakah C merupakan subtipe dari Type ? Coba kompilasi ini di Eclipse atau Idea Anda dan mereka akan memberi tahu Anda apa yang mereka pikirkan. Buang sia-sia... Beberapa tipe hubungan di Java tidak dapat diputuskan! 10. Tipe Persimpangan Java memiliki fitur yang sangat menarik yang disebut tipe persimpangan. Anda dapat mendeklarasikan tipe (generik) yang sebenarnya merupakan perpotongan dua tipe. Misalnya: Parameter tipe kustom T yang Anda kaitkan dengan instance kelas Test harus menyertakan antarmuka Serializable dan Cloneable. Misalnya, String tidak bisa dibatasi, tapi Tanggal bisa: Fitur ini memiliki banyak kegunaan di Java8, di mana Anda bisa mentransmisikan tipe. Bagaimana hal ini membantu? Hampir tidak ada, tetapi jika Anda ingin memasukkan ekspresi lambda ke tipe yang Anda butuhkan, maka tidak ada cara lain. Katakanlah Anda memiliki batasan yang sangat gila dalam metode Anda: Anda ingin Runnable yang pada saat yang sama dapat diserialkan hanya jika Anda ingin menjalankannya di tempat lain dan mengirimkan hasilnya melalui jaringan. Lambda dan serialisasi menambah sedikit ironi. Anda dapat membuat serial ekspresi lambda Anda jika tipe target dan argumennya dapat diserialkan. Namun meskipun ini benar, mereka tidak secara otomatis mengaktifkan antarmuka Serializable. Anda harus membawanya sendiri ke tipe ini. Tetapi ketika Anda hanya melakukan transmisi ke Serializable: maka lambda tidak lagi menjadi Runnable, jadi transmisikan keduanya ke kedua jenis: Dan sebagai kesimpulan: 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 4) D
class Test
// Doesn't compile Test
execute((Serializable) (() -> {}));
execute((Runnable & Serializable) (() -> {}));
GO TO FULL VERSION