public class Test { // throws: public static void main(String[] args) { doThrow(new SQLException()); } static void doThrow(Exception e) { Test.<RuntimeException> doThrow0(e); } @SuppressWarnings("unchecked") static <E extends Exception> void doThrow0(Exception e) throws E { throw (E) e; } }
SQLException
. You don't even need @SneakyThrows from Lombok. class Test { Object x() { return "abc"; } String x() { return "123"; } }
Note that a class may contain several suitable methods, because although the Java language does not allow declaring several methods with the same signature, the Java virtual machine still allows this if the return type is different. This flexibility of the virtual machine can be used to implement some of the language features. For example, a covariant return type can be implemented using a bridge method, which differs from the actual overloaded method only by the return type.Oh how! Yes, it sounds reasonable. In fact, this will happen if you write:
abstract class Parent<T> { abstract T x(); } class Child extends Parent<String> { @Override String x() { return "abc"; } }
Child
class: // Method descriptor #15 ()Ljava/lang/String; // Stack: 1, Locals: 1 java.lang.String x(); 0 ldc <String "abc"> [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
in bytecode simply turns into Object
. The synthetic bridge method is generated by the compiler, because in some places where the method is invoked, Object
can be expected as the return type of Parent.x()
. It would be difficult to add generic types without bridge methods and to provide binary compatibility. It turned out to be a lesser evil to modify the JVM so that it would support this possibility (and covariant return types appeared as a side effect). Clever, yes? 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 [] = {{}}; }
Type annotations. Mysterious and powerful mechanism . Steeper than its mysteriousness except its power .
My last commit before the monthly leave
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);
i += j; i = i + j;
The compound assignment operator of the form E1 op = E2 is equivalent to the expression E1 = (T) ((E1) op (E2)), where T is the type of E1, except that E1 is calculated only once.
An example of such a type conversion can be shown on * = or / =byte b = 10; b * = 5.7; System.out.println (b); // displays 57
orbyte b = 100; b = 2.5; System.out.println (b); // displays 40
orchar ch = '0'; ch * = 1.1; System.out.println (ch); // displays '4'
orchar ch = 'A'; ch * = 1.5; System.out.println (ch); // will print 'a'
for (int i = 0; i < 10; i++) { System.out.println((Integer) i); }
92 221 45 48 236 183 39 193 33 84
int goto = 1;
Test.java:44: error: <identifier> expected int goto = 1; ^
goto
is an unused keyword . Just in case, when it comes in handy.break
, continue
and blocks with labels: label: { // ... - ... if (check) break label; // ... - ... }
2 iload_1 [check] 3 ifeq 6 // 6 ..
label: do { // ... - ... if (check) continue label; // ... - ... break label; } while(true);
2 iload_1 [check] 3 ifeq 9 6 goto 2 // 9 ..
interface People => Set<Person>;
People
type is created in such a way that it can be used anywhere instead of Set<Person>
: People? p1 = null; Set<Person>? p2 = p1; People? p3 = p2;
Integer
, Long
, etc., we want to use short ones instead: I
and L
Easy: class Test<I extends Integer> { <L extends Long> void x(I i, L l) { System.out.println( i.intValue() + ", " + l.longValue() ); } }
I
is the alias for Integer
within the Test
class, and L
is the alias for Long
within the x()
method. We can safely call this method: new Test().x(1, 2L);
Integer
and Long
declared final, and this means that generic types I
and L
effectively aliases (well, almost: compatibility with assignment works only in one direction). If we used types that were not declared final (for example, Object
), then these would be ordinary generics. // . List interface Type<T> {} class C implements Type<Type<? super C>> {} class D<P> implements Type<Type<? super D<D<P>>>> {}
C
and D
types really mean?java.lang.Enum
. See: public abstract class Enum<E extends Enum<E>> { ... }
enum
declaration is syntactic sugar: // enum MyEnum {} // class MyEnum extends Enum<MyEnum> { ... }
class Test { Type<? super C> c = new C(); Type<? super D<Byte>> d = new D<Byte>(); }
C
is the Type<? super C>
Type<? super C>
Type<Type<? super C>>
Type<Type<? super C>>
is Type<? super C>
Type<? super C>
(inheritance)C
a Type<? super C>
Type<? super C>
(check mask? super C)D<Byte>
is Type<? super D<Byte>>
Type<? super D<Byte>>
Type<Type<? super D<D<Byte>>>>
Type<Type<? super D<D<Byte>>>>
is Type<? super D<Byte>>
Type<? super D<Byte>>
D<Byte>
is Type<? super D<D<Byte>>>
Type<? super D<D<Byte>>>
Type<Type<? super D<D<Byte>>>>
Type<Type<? super D<D<Byte>>>>
is Type<? super D<D<Byte>>>
Type<? super D<D<Byte>>>
D<D<Byte>>
Is Type<? super D<D<Byte>>>
Type<? super D<D<Byte>>>
Some relationships between types are not computable !
class Test<T extends Serializable & Cloneable> { }
T
corresponds to specific instances of the class Test
must implement both the Serializable
and Cloneable
. For example, the String
will not work, but the Date
is fine: // Test<String> s = null; // Test<Date> d = null;
<T extends Runnable & Serializable> void execute(T t) {}
Runnable
, which is also Serializable
in case you want to transfer it over the network and run somewhere else. In principle, lambda can be serialized:You can serialize a lambda expression if its target type and its captured arguments are serializable.But even if it is observed, the lambda will not automatically implement the
Serializable
marker interface. You will need a cast. But if you only lead to Serializable
: execute((Serializable) (() -> {}));
Runnable
. execute((Runnable & Serializable) (() -> {}));
Java is a mysterious and powerful mechanism. Steeper than its mysteriousness except its power.
Source: https://habr.com/ru/post/253787/
All Articles