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"; } }
Droite. Le langage Java ne permet pas de substituer deux méthodes de manière équivalente au sein de la même classe en même temps, quelles que soient leurs différences en termes de lancements ou de types de retour. Mais attendez une minute. Consultez à nouveau la documentation pour Class.getMethod(String, Class…). Il dit :
Notez qu'il est possible qu'il y ait plus d'une méthode correspondante dans une classe, car si le langage Java interdit plusieurs méthodes avec la même signature mais des types de retour différents, ce n'est pas le cas de la machine virtuelle Java. Cette flexibilité dans une machine virtuelle peut être utilisée pour implémenter diverses fonctionnalités de langage. Par exemple, les retours covariants peuvent être effectués avec des méthodes de pont ; La méthode pont et la méthode remplacée auraient la même signature mais des types de retour différents. Wow, c'est logique. Il se passe en fait beaucoup de choses lorsque vous écrivez ce qui suit : Regardez le bytecode généré : Il s'agit donc en fait d'un objet dans le bytecode. Cela est bien compris. La méthode du pont synthétique est en fait générée par le compilateur car le type de retour de Parent.x() peut être attendu à certaines parties des appels. L'ajout de génériques sans de telles méthodes de pont ne sera plus possible en représentation binaire. Ainsi, changer la JVM pour autoriser une telle fonctionnalité a produit moins de douleur (ce qui permet également le remplacement de la méthode covariante comme effet secondaire...) Intelligent, n'est-ce pas ?
3. Tous les éléments suivants sont des tableaux bidimensionnels. C'est en fait vrai. Même si votre analyseur mental ne peut pas comprendre immédiatement le type de retour des méthodes décrites ci-dessus, elles sont toutes identiques ! Comme le prochain morceau de code. Pensez-vous que c'est fou ? Le nombre d’occasions d’écrire fait également exploser l’imagination !
Tapez une annotation.
Un appareil dont le mystère n’a d’égal que sa puissance. Ou en d’autres termes : Quand je fais mon dernier engagement juste avant mes 4 semaines de vacances. Je vous donne la permission de l'utiliser comme vous le souhaitez.
4. Vous n'obtiendrez pas de conditionnel Alors, vous pensiez déjà tout savoir sur les conditions lorsque vous avez commencé à les utiliser ? Laissez-moi vous décevoir, vous aviez tort. La plupart d’entre vous penseront que les deux exemples suivants sont équivalents : équivalent à ceci ? Non. Utilisons un test rapide. Le programme affichera ce qui suit : Oui ! L'opérateur conditionnel effectuera des conversions de type si nécessaire. Parce que sinon, vous vous attendriez à ce que le programme lève une NullPointerException ?
5. Vous n'obtiendrez pas non plus d'opérateur d'affectation composé. La débrouillardise est-elle suffisante ? Examinons les deux extraits de code suivants :
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;
Intuitivement, ils devraient être égaux, n’est-ce pas ? Mais vous savez quoi, ils sont différents. La spécification JLS dit :
Une expression composée de type E1 op = E2 est équivalente à E1 = (T) ((E1) op (E2)), où T est le type de E1, sauf que E1 n'est évalué qu'une seule fois. Un bon exemple est d'utiliser *= ou /= :
byte b = 10; b *= 5.7; System.out.println(b); // prints 57
or: or
byte b = 100; b /= 2.5; System.out.println(b); // prints 40
:
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'
Alors, est-ce toujours un outil utile ?
6. Entiers aléatoires Passons maintenant à une tâche plus difficile. Ne lisez pas la solution. Voyez si vous pouvez trouver la réponse vous-même. Lorsque j'exécute le programme suivant :
for (int i = 0; i < 10; i++) { System.out.println((Integer) i); }
Parfois, j'obtiens le résultat suivant :
92 221 45 48 236 183 39 193 33 84
Mais comment est-ce possible ? Ok, la réponse réside dans le remplacement du cache Integer du JDK via la réflexion, puis dans l'utilisation du boxing et du déballage automatiques. Ne faites pas cela sans la permission d'un adulte ! Ou en d'autres termes :
7. GOTO Un de mes favoris. Java a GOTO ! Écrivez ceci :
int goto = 1;
et vous obtenez ceci : C'est parce que goto est un mot réservé inutilisé, juste au cas où... Mais ce n'est pas la partie la plus excitante. Ce qui est cool, c'est que vous pouvez inclure goto associé à des blocs break, continue et marqués : Sauter en avant Dans le byte code : Sauter en arrière Dans le byte code :
8. Java a des alias de type Dans d'autres langages (comme Ceylan), nous pouvons définir le type les alias sont très simples : la classe People ici est construite de telle manière qu'elle peut être interchangée avec un 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
Alors, que signifient C et D ? Dans un sens, ils sont récursifs, similaires à la récursion de java.lang.Enum. Considérez : étant donné les spécifications ci-dessus, l'implémentation réelle d'enum n'est qu'un sucre syntaxique : dans cet esprit, revenons à nos deux types. Le code suivant sera-t-il compilé ? Une question difficile... et qui n'est pas réellement résolue ? C est-il un sous-type de Type ? Essayez de compiler ceci dans votre Eclipse ou Idea et ils vous diront ce qu'ils en pensent. Jetez-le à la poubelle... Certaines relations de types en Java sont indécidables ! 10. Intersection de types Java possède une fonctionnalité très intéressante appelée intersection de types. Vous pouvez déclarer un type (générique) qui est en fait l'intersection de deux types. Par exemple : le paramètre de type personnalisé T que vous associez aux instances de la classe Test doit inclure à la fois les interfaces Serialisable et Cloneable. Par exemple, String ne peut pas être restreint, mais Date peut : Cette fonctionnalité a plusieurs utilisations dans Java8, où vous pouvez convertir des types. Comment cela aide-t-il ? Presque rien, mais si vous souhaitez convertir votre expression lambda dans le type dont vous avez besoin, il n'y a pas d'autre moyen. Disons que vous avez une telle limitation folle dans votre méthode : vous voulez Runnable qui en même temps est sérialisable uniquement si vous souhaitez l'exécuter à un autre endroit et envoyer le résultat sur le réseau. Lambda et la sérialisation ajoutent un peu d'ironie. Vous pouvez sérialiser votre expression lambda si son type cible et ses arguments sont sérialisables. Mais même si cela est vrai, ils n’activent pas automatiquement l’interface sérialisable. Vous devez les amener vous-même à ce type. Mais lorsque vous lancez uniquement un cast vers Serialisable : alors le lambda ne sera plus Runnable, alors lancez-les vers les deux types : Et en 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