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"; } }
Right. The Java language does not allow two methods to be overridden equivalently within the same class at the same time, regardless of their differences in throws or return types. But wait a minute. Check the documentation again for Class.getMethod(String, Class…). It says:
Note that it is possible that there is more than one corresponding method in a class, because while the Java language prohibits multiple methods with the same signature but different return types, the Java Virtual Machine does not. This flexibility in a virtual machine can be used to implement various language features. For example, covariant returns can be done with bridge methods; The bridge method and the overridden method would have the same signature but different return types. Wow, that makes sense. There's actually quite a lot going on when you write the following: Look at the generated bytecode: So t is actually an object in bytecode. This is well understood. The synthetic bridge method is actually generated by the compiler because the return type of Parent.x() can be expected at certain parts of the calls. Adding generics without such bridge methods will no longer be possible in binary representation. So, changing the JVM to allow such a feature produced less pain (which also allows covariant method overriding as a side effect...) Smart right?
3. All of the following are two-dimensional arrays. This is actually true. Even if your mental analyzer cannot immediately understand the return type from the methods described above, they are all the same! Like the next piece of code. Do you think this is crazy? The number of opportunities to write also simply explodes the imagination!
Type annotation.
A device whose mystery is second only to its power. Or in other words: When I make my last commit just before my 4 week vacation. I give you permission to use it in any way you like.
4. You won't get a conditional So, you thought you already knew everything about conditionals when you started using them? Let me disappoint you - you were wrong. Most of you will think that the following two examples are equivalent: equivalent to this? No. Let's use a quick test The program will output the following: Yes! The conditional operator will perform type casts if needed. Because otherwise you would expect the program to throw a NullPointerException?
5. You also won't get a compound assignment operator. Is resourcefulness enough? Let's look at the following two code snippets:
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;
Intuitively, they should be equal right? But you know what - they are different. The JLS specification says:
A compound expression of type E1 op = E2 is equivalent to E1 = (T) ((E1) op (E2)), where T is the type of E1, except that E1 is evaluated only once. A good example is to use *= 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'
So, is this still a useful tool?
6. Random integers Now for a more difficult task. Don't read the solution. See if you can find the answer yourself. When I run the following program:
for (int i = 0; i < 10; i++) { System.out.println((Integer) i); }
Sometimes I get the following output:
92 221 45 48 236 183 39 193 33 84
But how is this even possible? Ok, the answer lies in overriding the JDK's Integer cache via reflection, and then using auto-boxing and auto-unboxing. Don't do this without adult permission! Or in other words:
7. GOTO One of my favorites. Java has GOTO! Write this:
int goto = 1;
and you get this: That's because goto is an unused reserved word, just in case... But that's not the exciting part. The cool thing is that you can include goto paired with break, continue and marked blocks: Jumping forward In byte code: Jumping backward In byte code:
8. Java has type aliases In other languages (like Ceylon), we can define type aliases very easy: The People class here is built in such a way that it can be interchanged with a 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
So what do C and D even mean? In a sense they are recursive, similar to the recursion in java.lang.Enum. Consider: Given the specifications above, the actual implementation of enum is just syntactic sugar: With that in mind, let's return to our two types. Will the following code compile? A difficult question... and it's not actually being resolved? Is C a subtype of Type ? Try compiling this in your Eclipse or Idea and they will tell you what they think. Flush it down the drain... Some type relationships in Java are undecidable! 10. Type Intersection Java has a very interesting feature called type intersection. You can declare a (generic) type that is actually the intersection of two types. For example: The custom type parameter T that you associate with instances of the Test class must include both the Serializable and Cloneable interfaces. For example, String cannot be restricted, but Date can: This feature has multiple uses in Java8, where you can cast types. How does this help? Almost nothing, but if you want to cast your lambda expression into the type you need, then there is no other way. Let's say you have such a crazy limitation in your method: You want Runnable which at the same time is Serializable only if you want to execute it in another place and send the result over the network. Lambda and serialization add a bit of irony. You can serialize your lambda expression if its target type and arguments are serializable. But even if this is true, they do not automatically enable the Serializable interface. You must bring them to this type yourself. But when you cast only to Serializable: then the lambda will no longer be Runnable, so cast them to both types: And in conclusion: 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