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"; } }
Betul. Bahasa Java tidak membenarkan dua kaedah ditindih secara setara dalam kelas yang sama pada masa yang sama, tanpa mengira perbezaannya dalam jenis lontaran atau pengembalian. Tapi tunggu sekejap. Semak dokumentasi sekali lagi untuk Class.getMethod(String, Class…). Ia berkata:
Ambil perhatian bahawa ada kemungkinan terdapat lebih daripada satu kaedah yang sepadan dalam kelas, kerana walaupun bahasa Java melarang berbilang kaedah dengan tandatangan yang sama tetapi jenis pulangan yang berbeza, Mesin Maya Java tidak. Fleksibiliti dalam mesin maya ini boleh digunakan untuk melaksanakan pelbagai ciri bahasa. Sebagai contoh, pulangan kovarian boleh dilakukan dengan kaedah jambatan; Kaedah jambatan dan kaedah yang ditindih akan mempunyai tandatangan yang sama tetapi jenis pulangan yang berbeza. Wah, masuk akal. Sebenarnya banyak perkara yang berlaku apabila anda menulis perkara berikut: Lihat kod bait yang dijana: Jadi t sebenarnya adalah objek dalam kod bait. Ini difahami dengan baik. Kaedah jambatan sintetik sebenarnya dijana oleh pengkompil kerana jenis pemulangan Parent.x() boleh dijangka pada bahagian tertentu panggilan. Menambah generik tanpa kaedah jambatan sedemikian tidak lagi boleh dilakukan dalam perwakilan binari. Jadi, menukar JVM untuk membenarkan ciri sedemikian menghasilkan kesakitan yang kurang (yang juga membenarkan kaedah kovarian mengatasi sebagai kesan sampingan...) Pintar betul?
3. Semua yang berikut ialah tatasusunan dua dimensi. Ini sebenarnya benar. Walaupun penganalisis mental anda tidak dapat memahami dengan segera jenis pulangan daripada kaedah yang diterangkan di atas, semuanya adalah sama! Seperti sekeping kod seterusnya. Adakah anda fikir ini gila? Bilangan peluang untuk menulis juga hanya meletupkan imaginasi!
Taip anotasi.
Peranti yang misterinya kedua selepas kuasanya. Atau dengan kata lain: Apabila saya membuat komitmen terakhir saya sebelum cuti 4 minggu saya. Saya memberi anda kebenaran untuk menggunakannya dalam apa jua cara yang anda suka.
4. Anda tidak akan mendapat bersyarat Jadi, anda fikir anda sudah tahu segala-galanya tentang syarat apabila anda mula menggunakannya? Biar saya kecewakan awak - awak silap. Kebanyakan anda akan berfikir bahawa dua contoh berikut adalah setara: bersamaan dengan ini? Tidak. Mari gunakan ujian cepat Program ini akan mengeluarkan yang berikut: Ya! Operator bersyarat akan melakukan hantaran jenis jika perlu. Kerana jika tidak, anda akan mengharapkan program itu membuang NullPointerException?
5. Anda juga tidak akan mendapat pengendali tugasan kompaun. Adakah kepintaran cukup? Mari lihat dua coretan kod 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, mereka sepatutnya sama, bukan? Tetapi anda tahu apa - mereka berbeza. Spesifikasi JLS mengatakan:
Ungkapan majmuk jenis E1 op = E2 adalah bersamaan dengan E1 = (T) ((E1) op (E2)), dengan T ialah jenis E1, kecuali E1 dinilai sekali sahaja. Contoh yang baik ialah menggunakan *= atau /= :
byte b = 10; b *= 5.7; System.out.println(b); // prints 57
atau:
byte b = 100; b /= 2.5; System.out.println(b); // prints 40
atau:
char ch = '0'; ch *= 1.1; System.out.println(ch); // prints '4'
atau:
char ch = 'A'; ch *= 1.5; System.out.println(ch); // prints 'a'
Jadi, adakah ini masih alat yang berguna?
6. Integer rawak Sekarang untuk tugas yang lebih sukar. Jangan baca penyelesaiannya. Lihat jika anda boleh mencari jawapannya sendiri. Apabila saya menjalankan program berikut:
for (int i = 0; i < 10; i++) { System.out.println((Integer) i); }
Kadang-kadang saya mendapat output berikut:
92 221 45 48 236 183 39 193 33 84
Tetapi bagaimana ini mungkin? Ok, jawapannya terletak pada mengatasi cache Integer JDK melalui pantulan, dan kemudian menggunakan auto-tinju dan auto-nyahkotak. Jangan lakukan ini tanpa kebenaran orang dewasa! Atau dengan kata lain:
7. GOTO Salah satu kegemaran saya. Java mempunyai GOTO! Tulis ini:
int goto = 1;
dan anda mendapat ini: Itu kerana goto ialah perkataan terpelihara yang tidak digunakan, untuk berjaga-jaga... Tetapi itu bukan bahagian yang menarik. Perkara yang menarik ialah anda boleh memasukkan goto berpasangan dengan blok break, continue dan ditandai: Melompat ke hadapan Dalam kod bait: Melompat ke belakang Dalam kod bait:
8. Java mempunyai jenis alias Dalam bahasa lain (seperti Ceylon), kita boleh menentukan jenis alias sangat mudah: Kelas Orang di sini dibina dengan cara yang boleh ditukar ganti 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 apakah maksud C dan D? Dari satu segi, ia adalah rekursif, sama dengan rekursi dalam java.lang.Enum. Pertimbangkan: Memandangkan spesifikasi di atas, pelaksanaan sebenar enum hanyalah gula sintaksis: Dengan itu, mari kembali kepada dua jenis kami. Adakah kod berikut akan disusun? Soalan yang sukar... dan ia sebenarnya tidak diselesaikan? Adakah C subjenis Jenis ? Cuba kumpulkan ini dalam Eclipse atau Idea anda dan mereka akan memberitahu anda apa yang mereka fikirkan. Siramkannya ke dalam longkang... Sesetengah jenis perhubungan di Jawa tidak dapat ditentukan! 10. Type Intersection Java mempunyai ciri yang sangat menarik iaitu type intersection. Anda boleh mengisytiharkan jenis (generik) yang sebenarnya merupakan persimpangan dua jenis. Contohnya: Parameter jenis tersuai T yang anda kaitkan dengan kejadian kelas Ujian mesti termasuk antara muka Boleh Bersiri dan Boleh Diklon. Sebagai contoh, String tidak boleh dihadkan, tetapi Tarikh boleh: Ciri ini mempunyai berbilang kegunaan dalam Java8, di mana anda boleh menghantar jenis. Bagaimanakah ini membantu? Hampir tiada, tetapi jika anda ingin menghantar ungkapan lambda anda ke dalam jenis yang anda perlukan, maka tiada cara lain. Katakan anda mempunyai had yang gila dalam kaedah anda: Anda mahu Runnable yang pada masa yang sama boleh Serializable hanya jika anda mahu melaksanakannya di tempat lain dan menghantar hasilnya melalui rangkaian. Lambda dan bersiri menambah sedikit ironi. Anda boleh mensirikan ungkapan lambda anda jika jenis sasaran dan hujahnya boleh bersiri. Tetapi walaupun ini benar, mereka tidak mendayakan antara muka Serializable secara automatik. Anda mesti membawa mereka ke jenis ini sendiri. Tetapi apabila anda menghantar hanya ke Serializable: maka lambda tidak lagi boleh Runnable, jadi hantarkannya ke kedua-dua 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