La base de la programmation en Java


  • Administrateurs

    youtubeCe tutoriel est également disponible en vidéo.

    Sommaire

    Introduction

    Il n'est pas impossible de se lancer dans la création d'un mod Minecraft sans connaître la langage Java. Cependant c'est beaucoup plus compliqué. Et vous n'arriverez pas à faire des choses hors du commun, vous allez être limité à ce que les tutoriels permettent de faire. Moi-même j'ai commencé à faire des mods sans avoir appris le Java. Cependant mes premiers mods étaient très buggués et se limitaient à des copier/coller des tutoriels et du rafistolage.
    Je vous recommande de lire une première fois ce tutoriel pour savoir à quoi servent les différentes choses de Java. N'apprenez pas par cœur, ce genre de méthode ne fonctionne pas très bien. C'est en pratiquant régulièrement la programmation que les choses vont finir par rentrer. Lorsque vous suivrez les autres tutoriels du site je vous recommande de retaper le texte à la main plutôt que copier/coller. C'est beaucoup mieux pour travailler la mémoire. À partir de la 5/6ème fois où vous copierez quelque chose à la main vous allez la connaître par cœur et du-coup vous n'aurez plus besoin de revenir à chaque fois sur le tutoriel (et donc vous allez gagner beaucoup de temps). Alors que si vous vous contentez de faire des copier/coller jamais vous ne connaîtrez le code par cœur. Et vous allez perdre beaucoup de temps à chercher et relire les différents tutoriels). Pour cette raison je vous recommande de suivre les tutoriels vidéos si vous en avez la possibilité.

    Dans ce tutoriel nous allons voir seulement la base de Java. Uniquement ce qui va nous servir pour Minecraft. Nous n'allons par exemple pas voir comment créer une interface graphique en Java. Si vous souhaitez approfondir vos connaissances en Java pour créer autre chose que des mods Minecraft, je vous recommande le tutoriel d'openclassroom.

    Pré-requis

    La base de la programmation en Java

    Premier projet Java et première classe:

    Commencez par lancer Eclipse dans l'espace de travail (workspace en anglais) par défaut (ou une autre, comme vous voulez).
    Voici l'interface d'Eclipse :

    à gauche se trouve le package explorer, tous vos projets vont s’afficher ici ainsi que les différents packages et les différentes classes de vos projets. Au milieu c'est l'emplacement pour l'éditeur de texte. Lorsque vous allez ouvrir une classe, le code va s'afficher à cet endroit. En bas se trouve plusieurs onglets où vous allez trouver les erreurs Java, la console, la javadoc, etc … Si vous fermez par erreur une des vues, il suffit d'aller dans le menu Window puis "show view" et enfin choisir la vue à remettre. (vous pouvez aussi cliquer sur perspective -> reset perspective pour remettre l'interface par défaut).

    Dans la partie package explorer faites un clic droit puis "new" -> "project" -> déroulez le dossier Java -> "Java project". Cliquez sur next, choisissez un nom de projet puis cliquez sur "finish".
    Maintenant le projet créé, nous allons créer un premier package. Déroulez le projet afin de voir le dossier src. Faites un clic droit sur le dossier src puis "new" -> "package".
    Les packages vont permettre d'organiser votre mod / votre programme. Cela fonctionne comme des dossiers (et dans le fichier jar de votre mod ce sera des dossiers). Tout comme vous triez sûrement vos photos sur votre ordinateur, nous allons trier nos différents fichiers Java. Par exemple nous allons placer tous les fichiers en rapport avec des blocs dans un package blocs, ceux en rapport avec les items dans un dossier items, etc ...
    Par convention les packages sont nommés de la façon suivante :
    <extension du nom de domaine>.<nom de domaine>.<sous domaine si besoin>.<nom du projet>. <les différents sous dossier par thème>Chaque point correspondra à un sous-dossier. Dans mon cas par exemple ce sera :
    fr.minecraftforgefrance.tutoriel
    Tout le monde n'a pas de nom de domaine. Heureusement vous n'avez pas besoin de respecter cette conversion. Vous pouvez très bien utiliser <votre pseudo>.<nom du projet>.
    L'avantage d'avoir un nom de domaine c'est qu'on peut directement retrouver le propriétaire du programme. Dans le cas d'un mod je vous recommande de mettre au moins le nom du mod dans le package afin qu'on puisse localisé votre mod s'il cause un crash.
    Certaines personnes n'ayant pas de nom de domaine utilisent parfois leur adresse mail ou leur compte github (le pseudo est alors mis au niveau du sous-domaine). Exemple :
    com.github.robin4002. <nom du projet>com.gmail.robin4002. <nom du projet>Maintenant notre premier package créé nous allons créer une première classe. Faites un clic droit sur votre package puis "new" -> "class".
    Nous allons appeler cette classe "Main" car cela va être la mainclass de notre programme (classe principale, le fichier qui va être exécuté au lancement). Cochez la case "public static void main(String[] args) :

    Plus tard lorsque nous allons créer notre premier mod nous n'aurions pas besoin de cocher cette case. En effet cette case ajoute la méthode public static void main(String[] args) à la classe qui est une méthode exécutée au lancement d'un programme Java. Dans le cas de Minecraft cette classe principale se trouve dans Minecraft (dans le cas du jeu de base) ou dans une bibliothèque de Minecraft (LauncherWrapper dans le cas de Forge qui est une bibliothèque prévue pour modifier Minecraft et donc que Forge utilise). Nos mods auront juste une annotation nommée @Mod et FML détectera automatiquement la classe principale de notre mod (qui n'a rien à voir avec une classe principale d'un programme Java).
    Votre classe devrait ressembler à ceci (sans les commentaires que j'ai ajouté)

    package fr.minecraftforgefrance.tutoriel; // la déclaration du package
    
    public class Main { // la déclaration de la classe
    
        public static void main(String[] args) { // la méthode main, qui sera exécutée au lancement du programme
            // TODO Auto-generated method stub
        }
    }
    // un commentaire. Comme vous pouvez le voir les commentaires sont introduits par deux barre obliques.
    

    Les commentaires ne sont pas comptés comme du code et ne sont donc pas exécutés. Il peut être utile de commenter du code dans certains contextes (pour des tests ou autre). Pour faire ceci rapidement faites une sélection puis appuyez sur ctrl + shift + c (refaites ctrl + shift + c pour dé-commenter ensuite).

    En général le premier programme a pour but d'afficher "Hello world". Nous allons donc faire ceci. Dans la méthode main, à la place du commentaire ajouté automatiquement par eclipse, ajoutez :

    System.out.println("Hello world");
    

    Ce qui donne :

    package fr.minecraftforgefrance.tutoriel;
    
    public class Main {
    
        public static void main(String[] args) {
            System.out.println("Hello world");
        }
    }
    

    Lancez maintenant le programme en cliquant sur la flèche blanche entourée par un cercle vert. Le programme va se lancer et vous devrez voir dans la console "Hello world". Félicitation, vous venez de faire votre premier programme en Java !
    Bon, il ne fait pas grand chose mais c'est quand même un premier programme. Il faut bien commencer par quelque chose de simple. D'ailleurs à propos de commencement, lorsque l'on débute, on fait forcément des erreurs et il y a forcément des choses que l'on ne comprend pas. N’hésitez pas à faire quelques recherches ou à demander des explications. Concernant les erreurs, elles sont inévitables et c'est grâce à elles que l'on progresse. D'ailleurs on peut aussi apprendre des erreurs des autres.
    Par exemple voici quelques erreurs que j'ai déjà vu sur les forums :
    On ne peut pas placer des instructions n'importe où. Lorsqu'on appelle une méthode (par exemple lorsqu'on écrit l'instruction System.out.println("Hello world"); on appelle la méthode println avec comme argument "Hello world") cela ne peut que être fait dans une autre méthode.

    package fr.minecraftforgefrance.tutoriel;
    
    public class Main {
    
        public static void main(String[] args) {
            System.out.println("Hello world");
        }
    }
    

    Le code ci-dessus fonctionne, comme nous l'avons vu plus tôt.

    package fr.minecraftforgefrance.tutoriel;
    
    public class Main {
    
        public static void main(String[] args) {
        }
        System.out.println("Hello world");
    }
    

    Le code ci-dessus ne fonctionnera pas et causera une erreur.

    package fr.minecraftforgefrance.tutoriel;
    
    public class Main {
    
        public static void main(String[] args) {
        }
    
        public static void uneMethode() {
            System.out.println("Hello world");
        }
    }
    

    Le code ci-dessus ne causera pas d'erreur mais cependant n'affichera pas "Hello world". En effet la méthode (ou fonction) "uneMethode" n'est jamais appelée.

    package fr.minecraftforgefrance.tutoriel;
    
    public class Main {
    
        public static void main(String[] args) {
            uneMethode();
        }
    
        public static void uneMethode() {
            System.out.println("Hello world");
        }
    }
    

    Le code ci-dessus en revanche fonctionnera, puisque la méthode uneMethode() est appelée dans la méthode main (celle qui est exécutée lors du lancement du programme).
    Ici ma méthode uneMethode n'a pas vraiment d'intérêt puisqu'il n'y a qu'une seule instruction dedans. Cependant cela peut se montrer utile si vous avez plusieurs instructions que vous voulez regrouper dans une méthode pour augmenter la lisibilité du code. Et ça l'est encore plus si nous avons besoin de répéter plusieurs fois cette méthode (il suffira juste de l'appeler).

    Toutes les méthodes que nous avons vu jusqu'à maintenant sont du type "void" (comme vide). En effet elles sont dites vides car elles n'ont pas de retour. Nous verrons plus tard que l'on peut donner un résultat à notre méthode (ou un retour) qui pourra être utilisé pour affecter une valeur à une variable. Il est aussi possible aussi d'ajouter des arguments à une fonction. Mais avant de vouloir faire tout cela il faut voir ce qu'est une variable, quels sont les différents types de variables et ce que l'on peut faire avec.

    Les différentes variables et les opérateurs :

    Une variable est une référence à une donnée. Elle peut prendre plusieurs valeurs différentes en fonction de son type.

    • Le** byte** est une variable composée de 8 bits. Un bit peut prendre soit la valeur 0, soit la valeur 1. Dans l'ordinateur le 0 correspondra l'absence de courant alors que le 1 est la présence de courant. Comme un bit ne peut prendre que 2 valeurs et qu'un byte est composé de 8 bits cela donne 2^8 valeurs possibles (^ = puissance en informatique). Soit 256 valeurs différentes. Le byte pourra donc prendre une valeur entre -128 inclus et 127 inclus, ce qui donne bien 256 valeurs différentes (128 valeur négatif + 127 valeur positif + le 0). Il a l'avantage de prendre peu de place en mémoire.
    • Le **short **est une variable composée de 16 bits. Soit 2^16 = 65536 valeurs différentes de -32768 inclus à 32767 inclus. Il est donc beaucoup plus grand que le byte mais prend deux fois plus de place en mémoire.
    • L'integer (int) est composé de 32 bits, soit 2^32 = 4294967296 valeurs différentes possibles, de -2147483648 inclus à 2147483647 inclus.
    • Le long est composé de 64 bits, soit 2^64 = 18446744073709551616 valeurs différentes possibles, de -9223372036854775808 inclus à 9223372036854775807 inclus. Ça fait beaucoup 😉

    Toutes ces variables sont des nombres entiers. Donc pas de virgule possible.
    Pour cela on a deux autres variables utilisant la norme IEEE 754 :

    • Le float, un nombre à virgule flottante encodé sur 32 bits. Il peut prendre des valeurs comprises entre -3.4028235E38 et -1.4E-45 ainsi qu'entre 1.4E-45 et 3.4028235E38 ainsi que la valeur 0. On peut donc avoir de très grand nombre à virgule mais aussi des très petit nombre.
    • Le double, un nombre à virgule flottante encodé sur 64 bits. La valeur la plus petite est de 4.9E-324 et la plus de grande de 1.7976931348623157E308. (on peut donc avoir des nombres beaucoup plus grand et beaucoup plus petit d'avec des floats).

    Le E étant 10 puissance on se passera de l'écriture non scientifique 😄

    Toutes ces variables sont des primitives. Elles s’afficheront en violet sur Eclipse. L'avantage des primitives c'est qu'elles sont beaucoup plus rapides que les objets que nous allons voir plus tard. Il manque juste deux primitives dans cette liste. L'une d'elle est le caractère. Elle se note char.

    Les variables peuvent être déclarées de deux façons différentes. Soit c'est une variable globale à la classe (aussi appelée field en anglais) et dans ce cas elle sera déclarée juste en dessous de l'accolade après la déclaration de la classe. Soit c'est une variable locale à une fonction et dans ce cas elle est déclarée à l'intérieur d'une fonction.
    Une variable globale pourra être réutilisée partout dans la classe voir même dans d'autres classes si elle est déclarée comme public (accès possible de partout) ou protected (accès possible seulement depuis le même package ou dans une classe fille (on verra l'héritage un peu plus tard)). Si elle est déclaré comme private, elle ne pourra être utilisé que dans la classe. Les variables privées ne peuvent que être utilisées dans la classe même.
    Une variable locale ne pourra que être utilisée dans la fonction dans laquelle elle se trouve.
    Pour déclarer une variable la syntaxe est la suivante :
    <modificateur d'accès><type de variable><nom de la variable>;
    Le modificateur d'accès est seulement à mettre si la variable est globale à la classe. Si vous le mettez dans une variable locale vous allez obtenir une erreur.
    On peut aussi directement affecter une valeur avec la syntaxe suivante :
    <modificateur d'accès><type de variable><nom de la variable>= valeur de la variable;
    Par ailleurs si on ajoute le modificateur "final" la variable ne pourra plus être modifiée.

    Quelques exemples, je pense que du concret est la bienvenue après autant de théorie :

    package fr.minecraftforgefrance.tutoriel;
    
    public class Main {
    
        public int unNombre = 1452;
        public float unNombreAvirgule = 4.5F; // Il faut mettre un point et non une virgule. Il faut également ajouter un F comme Float
    
        public static void main(String[] args) {
    
            byte b = 04;
            System.out.println(b); // on peut afficher notre byte
    
            double unNombreAvirguleEnorme = 400000000000000000000000000000000000000000000000000000000.58865489465465D; // idem, un . et un D comme Double cette fois.
            System.out.println(unNombreAvirguleEnorme); // on peut afficher notre double
    
            char unCaractere = 'm'; // les caractères doivent être encadrés par des apostrophes.
            System.out.println(unCaractere); // on peut afficher notre caractère
            unCaractere = 'f';  // on peut aussi lui affecter une autre valeur (aussi possible pour les autres variables). En revanche ce n'est plus possible si on ajoute final devant char unCaractere = 'm';
            System.out.println(unCaractere); // et afficher cette autre valeur
        }
    }
    

    Jetez un coup d’œil aux commentaires du code, ils contiennent des informations importantes.

    Bon c'est bien joli d'avoir des variables avec des valeurs dedans. Mais qu'est-ce que l'on peut faire avec ces variables ?

    On va pouvoir les additionner, les soustraire, les multiplier, les diviser, bref, faire des mathématiques ! Et oui car l'informatique ce sont des mathématiques. Simple exemple, lorsque vous êtes dans Minecraft et que vous appuyez sur la touche z de votre clavier. Tant que vous allez rester appuyé dessus on va modifier les coordonnées du joueur en fonction de votre angle de vue. Il y a donc 5 variables, votre position en x (un double) votre position en y (aussi un double) votre position en y (aussi un double) et deux angles (des floats si ma mémoire est bonne, sinon aussi des doubles). Et avec quelques calculs complexes on peut savoir quelles sont les nouvelles coordonnées x, y, z du joueur. (bon en réalité c'est plus complexe car il y a des valeurs d’accélérations sur chaque axe). Et il y a des calculs presque partout.

    Nous allons commencer par des calculs simples :

    package fr.minecraftforgefrance.tutoriel;
    
    public class Main {
    
        public static void main(String[] args) {
            final int i = 5;
            final int j = 10;
            int k = i + j; // 5 + 10 = 15
            System.out.println(k);
            k = i - j; // 5 - 10 = -5
            System.out.println(k);
            k = i * j; // 5 * 10 = 50
            System.out.println(k);
            k = j / i; // 10 / 5 = 2
            System.out.println(k);
            k = i / j; // 5 / 10  = 0.5 mais comme les int ne supportent pas les virgules, on obtient 0
            System.out.println(k);
            k = (int) Math.pow(j, i); // 10 puissance 5 = 100000
            System.out.println(k);
            k = 50 % 7; // division euclidienne de 50 par 7 = le reste de la division de 50 par 7\. Comme 7*7 = 49 et 7*7+1 = 50, 50 % 7 = 1
            System.out.println(k);
        }
    }
    

    Et le résultat dans la console :

    15
    -5
    50
    2
    0
    100000
    1
    

    Comme vous pouvez le voir il n'y a pas de signe pour faire puissance. On doit utiliser Math.pow(base, exposant). D'ailleurs le résultat est un double. Comme mon nombre k est un int, je dois cast le double en int (c'est pour ça qu'il y a écrit (int) devant Math.pow).

    La division euclidienne peut être utilisée pour vérifier si un nombre est multiple d'un autre. Par exemple pour vérifier si un nombre est multiple de deux (donc s'il est pair) on vérifie que la division euclidienne par deux est égale à 0.
    Justement et si on créait une petite fonction pour savoir si un nombre est pair ?

        public static boolean estPair(int nombre) {
            return nombre % 2 == 0;
        }
    

    Exemple d'utilisation :

    package fr.minecraftforgefrance.tutoriel;
    
    public class Main {
    
        public static void main(String[] args) {
            System.out.println(estPair(48));
            System.out.println(estPair(25));
        }
    
        public static boolean estPair(int nombre) {
            return nombre % 2 == 0;
        }
    }
    

    Et le résultat dans la console :

    true
    false
    

    "Oh punaise qu'est-ce que c'est tout cela !!?!" vous allez me dire.
    Ah oui mince, j'ai en effet augmenté la complexité du code.
    Si vous avez bien suivi le tutoriel vous avez sûrement remarqué que je n'ai pas parlé de la dernière primitive. La voilà maintenant. C'est la valeur booléenne (boolean en anglais). Elle peut soit être vraie soit être fausse :

    package fr.minecraftforgefrance.tutoriel;
    
    public class Main {
    
        public static void main(String[] args) {
            boolean b = true; // elle est vrai
            System.out.println(b);
            b = false; // maintenant elle est fausse
            System.out.println(b);
        }
    }
    

    Rien de compliqué. D'ailleurs si vous n'initialisez pas la valeur (si vous ne mettez pas de = false ou true) :

    package fr.minecraftforgefrance.tutoriel;
    
    public class Main {
    
        public static boolean b;
        public static void main(String[] args) {
            System.out.println(b);
        }
    }
    

    Elle vaudra par défaut false. Les variables int, short, etc … vaudront 0 par défaut. Attention, seules les variables globales pourront ne pas être initialisées (car elles seront initialisées automatiquement sur leurs valeurs par défaut lors du chargement de la classe). Les variables locales devront toujours être initialisées avant d'être utilisées.

    Revenons sur la fonction que je vous ai montré juste avant :

        public static boolean estPair(int nombre) {
            return nombre % 2 == 0;
        }
    

    Comme vous pouvez le voir cette fonction n'est pas du type void mais du type boolean. Cela veut dire qu'elle donnera un résultat qui sera du type boolean. Elle a également un argument de type int que j'ai nommé nombre. Dans ma fonction je peux ensuite ré-utiliser cet argument.
    nombre % 2 == 0 est une valeur booléenne. Cela revient au même que Boolean.valueOf(nombre % 2 == 0); c'est juste plus court et donc plus pratique. Le double égal permet de tester l'égalité. Si le reste de nombre divisé par 2 est 0 ce sera true, sinon false.
    Cette structure "si ... sinon" nous amène à la partie suivante, les conditions.

    Les conditions :

    Les conditions ont besoin d'une valeur booléenne. Elles se présentent sous la forme suivante :

    if(valeurBooléenne)
    {
        codeÀExécuterSiLaConditionEstVrai();
    }
    

    Il est possible d'ajouter l'instruction else (sinon) pour exécuter une action si la valeur booléenne est fausse :

    if(valeurBooléenne)
    {
        codeÀExécuterSiLaValeurBooléenneEstVrai();
    }
    else
    {
        codeÀExécuterSiLaValeurBooléenneEstFausse();
    }
    

    Il existe plusieurs opérateurs pour comparer deux nombres.
    Pour vérifier l'égalité entre deux nombres, on utilise ==. Il faut bien mettre deux fois le caractère égal pour éviter la confusion avec le simple égal qui sert à affecter une valeur à une variable.

    boolean b = (1 == 1); // b = true comme 1 est bien égal à 1
    

    Les autres opérateurs :
    a > b : vérifie que a est strictement supérieur à b
    a < b : vérifie que a est strictement inférieur à b
    a >= b : vérifie que a est supérieur ou égal à b
    a <= b : vérifie que a est inférieur ou égal à b
    Comme vu dans l'exemple plus haut on peut récupérer la valeur dans une variable de type booléenne. Ou alors on peut l'utiliser directement dans un if :

    if(a == 0)
    {
        System.out.println("le nombre est nul");
    }
    else if(a > 0)
    {
        System.out.println("le nombre est positif");
    }
    else
    {
        System.out.println("le nombre est négatif");
    }
    

    J'en ai également profité pour utiliser l'instruction else if (sinon si), l'exemple vous a sûrement permis de comprendre son fonctionnement.

    Si on veut vérifier que a n'est pas nul et que b est positif on pourrait faire comme ceci :

    if(a == 0)
    {
        // a est null
    }
    else
    {
        if(b >= 0)
        {
            System.out.println("a n'est pas nul est b est positif");
        }
    }
    

    Cependant ce code est long et compliqué à lire.
    Heureusement il existe des opérateurs pour les valeurs booléennes :
    a = !b: a vaut l'inverse de b
    a = b && c : a vaut true si b et c vaut true.
    a = b || c : a vaut true si b ou c vaut true
    Plutôt que tester une égalité et obtenir son inverse ( exemple if(!(a == 0)) ) on peut tester directement une inégalité avec **!= **( exemple if(a != 0) )

    On va donc pouvoir refaire la même chose qu'avant mais en mieux :

    if(a != 0 && b >= 0)
    {
        System.out.println("a n'est pas nul et b est positif");
    }
    

    C'est déjà mieux comme ça 😄

    Les conditions peuvent se présenter sous une deuxième forme. Cette forme se nomme condition ternaire. Elle est très pratique pour une initialisation d'une variable en fonction d'une condition ou pour les retours de fonction.
    Petit exemple : on veut que la variable i ait pour valeur 0 si la booléenne b est vraie et pour valeur 2 si b est fausse. On peut donc faire ceci :

    int i = 2;
    if(b)
    {
        i = 0;
    }
    

    Premier cas, b vaut vrai. On affecte la valeur 2 à i. b est vrai, donc on affecte 0 a i. Résultat : i vaut 0, ça fonctionne.
    Deuxième cas, b vaut faux. On affecte la valeur 2 à i. b est faux, on passe. Résultat i vaut 2, ça fonctionne.
    Le code fonctionne bien comme on veut. MAIS, il fait 4 ligne. Et dans le cas où b est vrai i est initialisé à 2 pour être ensuite initialisé à 2. (ce qui donne un total de deux initialisation). Ici i est une primitive donc ce n'est pas très grave, mais faire ceci avec des objets a un impact sur les performances (petit impact, mais il y en a quand même).
    On pourrait sinon faire comme ceci :

    int i;
    if(b)
    {
        i = 0;
    }
    else
    {
        i = 2;
    }
    

    Plus optimisé mais prend encore plus de place.

    Et c'est là qu'interviennent les conditions ternaires :

    int i = b ? 0 : 2;
    

    Et voila ! Une seule ligne pour la même chose. Génial non ?
    Une condition ternaire se présente sous la forme suivante :
    variable = condition ? si la condition est vraie : si la condition est fausse
    Le ? sert de if, la seule différence c'est que la condition est avant le if au lieu d'être après le if. Ensuite le : sert de else. On peut utiliser les conditions ternaires pour affecter une valeur à une variable en mettant directement la valeur mais on peut très bien appeler une méthode :

    public int getValue(int i)
    {
       return i % 2 == 0 ? getValue1(i) : getValue2(i);
    }
    
    public int getValue1(int i)
    {
        return i/2;
    }
    
    public int getValue2(int i)
    {
        return i*2;
    }
    

    *(Vous pouvez constater que j'ai beaucoup d'imagination pour le nom des mes fonctions)
    *Dans cette exemple si j'appelle la fonction getValue(int i) en mettant comme argument un nombre pair, il va renvoyer ce qui se trouve dans getValue1 donc mon nombre divisé par deux. Si le nombre mis en argument est impair elle va appeler la fonction getValue2 qui va retourner ce nombre multiplié par deux.
    Bon ici ça ne sert pas à grand chose puisque getValue1 et getValue2 ne font qu'un petit code. On aurait pu faire comme ceci :

    public int getValue(int i)
    {
       return i % 2 == 0 ? i/2 : i*2;
    }
    

    Mais si vous avez besoin d'un code beaucoup plus long, votre code sera beaucoup plus lisible avec ce que je vous ai montré juste avant (surtout si vous avez besoin d'utiliser les deux autres méthodes pour d'autres choses).

    Nous allons voir une dernière chose dans cette partie. Il vous arrivera sûrement un jour d'avoir besoin de faire une action spécifique pour chaque valeur d'un nombre (le premier exemple qui me vient à l'esprit c'est un bloc avec metadata, il peut arriver que chaque metadata ait des différences).
    Une solution est donc d'utiliser plusieurs else if :

    if(i == 0)
    {
       // action si i vaut 0
    }
    else if(i == 1)
    {
       // action si i vaut 1
    }
    else if(i == 2)
    {
       // action si i vaut 2
    }
    etc …
    

    Il existe une solution beaucoup simple. Utiliser switch :

    switch(i)
    {
        case 0:
                  // action si i vaut 0
                  break;
        case 1:
                  // action si i vaut 1
                  break;
        case 2:
                  // action si i vaut 2
                  break;
        default:
                  // action par défaut (donc si non trouvé dans les différentes "case")
                  break;
    }
    

    Le mot break est important. C'est lui qui sert à sortir du switch. Si vous le ne mettez pas l'instruction suivante va être exécutée. En gros si i vaut 0 et qu'on ne met pas les switch, il va faire l'action dans la case 0, celle dans la case 1, celle dans la case 2 et celle dans la case default.
    Les switch ne peuvent que être utilisé avec les primitives et avec les énumérations (on verra ce que c'est dans une prochaine partie du tutoriel). Sous Java 7 il est possible d'utiliser les switch avec des chaînes de caractères.

    Les boucles :

    Il arrive très souvent en programmation qu'il soit nécessaire de répéter plusieurs fois une action. D'où l’existence des boucles.
    La première boucle que nous allons voir est la boucle for (pour).
    En Java elle prend la syntaxe suivante :

    for(initialisation;condition à valider pour continuer;instruction à chaque tour)
    {
        // code à exécuter
    }
    

    Exemple :

    for(int i = 0; i < 10; i++)
    {
        System.out.println(i);
    }
    

    Ce code va écrire dans la console tous les chiffres (0 - 9).
    En effet dans la partie initialisation i prend la valeur 0. À chaque tour on incrémente i de 1 (i++ augmente de 1 la valeur de i et i– diminue la valeur de i de 1). Et on fait cette incrémentation tant que i est strictement inférieure à 10.
    De façon plus détaillée ça donne :

    • i prend la valeur 0. 0 est bien strictement inférieur à 10, alors on fait ce qui se trouve dans la boucle (donc afficher i, qui vaut 0 à ce moment).
    • on incrémente i de 1, 0 + 1 = 1 donc i vaut maintenant 1. 1 est bien strictement inférieur à 10, on affiche 1.
    • on incrémente i de 1, 1 + 1 = 2 donc i vaut maintenant 2. 2 est bien strictement inférieur à 10, on affiche 2.
      [..]
    • on incrémente i de 1, 8 + 1 = 9 donc i vaut maintenant 9. 9 est bien strictement inférieur à 10, on affiche 9.
    • on incrémente i de 1, 9 + 1 = 10 donc i vaut maintenant 10. 10 n'est pas strictement inférieur à 10, on sort donc de la boucle.

    On peut faire la même chose mais dans l'autre sens (afficher les chiffes en partant de 9 et en arrivant à 0) :

    for(int i = 9; i >= 0; i–)
    {
        System.out.println(i);
    }
    

    Cette fois on vérifie que i est supérieur ou égal à 0, sinon il n'affichera pas 0.

    Dans la partie sur les conditions nous avons vu l'instruction switch. Avec elle nous avons également vu break, nécessaire pour arrêter l'instruction switch. break peut aussi être utilisé dans une boucle for :

    for(int i = 0;; i++)
    {
        System.out.println(i);
        if(i >= 50)
        {
            break;
        }
    }
    

    Avec cette boucle on affiche tous les nombres de 0 à 50 compris. Comme vous pouvez le voir ici il n'y a pas de condition à valider pour continuer dans la boucle. Cela ne cause pas d'erreur, la boucle va juste être exécutée à l'infini. Ou pas, puisque si i est supérieur ou égal à 50 on arrête la boucle avec l'instruction break. Sans le break le code ne sera pas non plus erroné mais si vous l'exécutez votre programme ne prendra jamais fin (et va finir par planter).
    Vous ne voyez sûrement pas l'intérêt d'utiliser break ici puisque faire comme ceci :

    for(int i = 0; i <= 50; i++)
    {
        System.out.println(i);
    }
    

    revient au même.
    Plus tard nous allons voir les tableaux et les listes. Les boucles for sont très pratiques pour parcourir ces derniers. Il peut parfois être nécessaire de stopper la boucle en fonction d'une valeur se trouvant dans un tableau ou dans une liste. L'instruction break est nécessaire dans ce cas.

    Une autre instruction qui peut se monter utile, l'instruction continue. Elle permet de sauter une valeur de la boucle. Ainsi le code suivant :

    for(int i = 0; i < 8; i++)
    {
        if(i % 3 == 0) // si le reste de la division par 3 est 0 (donc si le nombre est divisible par 3)
        {
            continue; // on passe directement à la suite.
        }
        System.out.println(i);
    }
    

    Affichera 1 puis 2 puis 4 puis 5 et enfin 7. 0, 3, et 6 n'apparaissent pas car ils sont divisibles par 3.

    La deuxième boucle est la boucle tant que (while).
    C'est un peu comme une boucle for mais sans l'initialisation et l'instruction à chaque tour.
    Il y a deux syntaxes possibles :

    while(condition)
    {
        // code à exécuter
    }
    

    ou :

    do
    {
        // code à exécuter
    }
    while(condition);
    

    Dans la première syntaxe la condition sera vérifiée avant d'exécuter le code. Dans la deuxième syntaxe le code est d'abord exécuté puis la condition est vérifiée.
    Deux petits exemples pour illustrer la différence :

    int i = 1;
    do
    {
        i *= 2; // revient au même que faire i = i * 2; donc i est multipliée par deux
    }
    while(i < 6);
    System.out.println(i);
    

    int i = 1;
    while(i < 6)
    {
        i *= 2; // revient au même que faire i = i * 2; donc i est multipliée par deux
    }
    System.out.println(i);
    

    Ici le résultat sera exactement le même.
    En effet dans le premier cas on multiplie i par 2, elle vaut donc 2. On vérifie la condition, "2 < 6" est vraie donc on reprend, i devient donc 4, "4 < 6" est vrai donc on reprend encore i devient 8, "8 < 6" est faux on sort donc de la boucle et on affiche 8, résultat 8.
    Dans le deuxième cas 1 est bien inférieur à 6, donc on multiplie par deux, i devient 2, "2 < 6" est vrai on multiplie encore par deux, i devient 4, "4 < 6" est vrai donc on refait encore le contenu de la boucle, i devient 8, "8 < 6" est faux on sort donc de la boucle et on affiche 8, résultat 8.

    Deuxième exemple :

    int i = 4;
    do
    {
        i *= 2;
    }
    while(i < 4);
    System.out.println(i);
    

    int i = 4;
    while(i < 4)
    {
        i *= 2;
    }
    System.out.println(i);
    

    Dans le premier cas on multiplie i par 2, i devient donc 8. On vérifie la condition "8 < 4", elle est fausse donc on sort de la boucle.
    Dans le deuxième cas on vérifie la condition "4 < 4" elle est fausse donc on sort de la boucle. Et on a rien fait, donc i vaut toujours 4.

    Vous devriez avoir compris maintenant la différence. En fait avec "do while" le code est exécuté une fois même si la condition qui se trouve dans le while est fausse dès le départ. Ce qui n'est pas le cas avec juste "while".

    Les instructions break et continue peuvent aussi être utilisées dans une boucle while.
    Il est possible de faire des boucles for vides ( for(;;) ) et dans boucle while(true) pour faire des boucles infinies.

    Dans certains cas on a des boucles dans des boucles. Les instructions break et continue ont toujours effet sur la boucle qui est en train d'être exécutée (donc la plus proche). Exemple :

    for(int i = 0; i < 50; i += 2) // boucle 1
    {
        for(int j = 0; i < 50; j++) // boucle 2
        {
            if(i == j)
            {
                continue;
            }
            System.out.println(i + " " + j);
        }
    }
    

    L'instruction continue va agir sur la boucle 2. Pour qu'elle agisse sur la boucle 1 il faut utiliser les labels :

    boucle1 : for(int i = 0; i < 50; i += 2) // boucle 1
    {
        for(int j = 0; i < 50; j++) // boucle 2
        {
            if(i == j)
            {
                continue boucle1;
            }
            System.out.println(i + " " + j);
        }
    }
    

    (dans cet exemple break la boucle 2 aurait donné la même chose).
    Comme vous pouvez le voir rien de compliqué, il suffit de choisir un nom puis mettre un point virgule et place tout ça devant l'instruction for (fonctionne aussi avec while).

    Objets et instances :

    Java est un langage orienté objet. Un objet en programmation peut-être tout et n'importe quoi. Dans le code de Minecraft ces objets pourront être des items, des blocs, des joueurs, des zombies, des messages de tchat, etc …
    Ces objets vont correspondre à une instance d'une classe.
    Dans les bibliothèques de Java il y a un paquet d'objets. Un des objets qu'on utilise le plus est le String (chaîne de caractères). Les chaînes de caractères s'écrivent entre guillemets :

    String s = "une chaîne de caractère";
    

    Comme vous pouvez le voir contrairement aux primitives qui s'affiche en violet (par défaut) sur Eclipse les objets restent blanc.
    Il existe pour chaque primitive une version sous forme d'objet (Integer pour int, Long pour long, etc ...).
    Il est important de savoir que :

    • comparer deux objets avec l'opérateur == compare l'instance de ces objets est identique. Pour cette raison il faut utiliser la fonction equals pour comparer deux objets :
            Integer a = new Integer(2);
            Integer b = new Integer(2);
            System.out.println(a == b);
    

    affichera false dans la console.
    Alors que :

            Integer a = new Integer(2);
            Integer b = new Integer(2);
            System.out.println(a.equals(b));
    

    Affichera true. Dans ce cas on aura de toute façon plutôt utilisé directement int (on va tout de suite voir pourquoi) et donc évité le problème, mais pensez à toujours utiliser equals avec les chaînes de caractères ou autres objets.

    • les objets sont plus lents que les primitives (car ils sont aussi plus complexes). Donc si vous avez le choix entre faire un objet qui contient plusieurs primitives et directement utiliser plusieurs primitives le deuxième choix sera souvent le meilleur (le premier sera en revanche plus pratique pour être placé dans une liste).
      Mettez ceci dans la fonction main de votre programme puis lancez-le :
            long start = System.currentTimeMillis();
            Long sum = 0L;
            for (int i = 0; i < Integer.MAX_VALUE; i++) {
                 sum += i;
            }
            long end = System.currentTimeMillis();
            System.out.println(sum);
            System.out.println("temps de calcul " + (end - start) + " ms");
    

    Cela va additionner toutes les valeurs comprises entre 0 et le maximum d'un int puis afficher le temps qu'il a fallus pour faire le calcul. Sur mon (vieux) pc portable cela met environ 11 secondes. Maintenant remplacez le L majuscule de Long par un l minuscule pour utiliser une primitive au lieu d'un objet (sur la ligne Long sum = 0L). Maintenant le programme s’exécute chez moi en 1,7 secondes. Voila, maintenant vous avez compris pourquoi il vaut mieux utiliser les primitives 😉

    Mais les objets n'existent pas pour rien, ils sont en effet très pratiques. Prenons de-nouveau un exemple lié à Minecraft. Comment définir un joueur dans du code ? Un joueur a pour commencer un pseudo. Il y a aussi une position, Minecraft étant en 3D il y a 3 axes, donc il faut 3 variables (x, y, z). Mais ce joueur a aussi une vitesse de déplacement, de la vie, de la faim, etc ... Il faudrait beaucoup de variable pour définir un joueur. C'est pour ça qu'on va utiliser un objet et dans cette objet mettre toutes ces variables.
    Comme dit plus tôt un objet est l'instance d'une classe. Donc nous allons créer une nouvelle classe. Appelons-la Player. On va ajouter à cette classe une variable pour le nom et 3 pour la position (on ne va pas faire tout le reste, c'est juste un exemple simplifié) :

    package fr.minecraftforgefrance.tutoriel; // vous allez avoir autre chose ici
    
    public class Player
    {
        public String name;
        public int x;
        public int y;
        public int z;
    }
    

    Et voilà ! Cependant toutes nos variables sont nulles, on va donc ajouter un constructeur qui va permettre de leur donner des valeurs. Cliquez sur le menu source puis cliquez sur Generate constructor using fields. Laissez les paramètres par défaut et faites ok :

    package fr.minecraftforgefrance.tutoriel;
    
    public class Player
    {
        public String name;
        public int x;
        public int y;
        public int z;
    
        public Player(String name, int x, int y, int z)
        {
            super();
            this.name = name;
            this.x = x;
            this.y = y;
            this.z = z;
        }
    }
    

    Notre classe a maintenant un constructeur. On peut retirer l'instruction "super();" elle ne sert pas ici. Nous allons voir dans la partie suivante son utilité.
    Retournons dans la classe principale, on va créer deux instances de notre classe Player :

    public class Main
    {
        public static void main(String[] args)
        {
            Player robin = new Player("robin4002", 1, 2, 6);
            Player inconnu = new Player("inconnu", 1, -3, 8);
            robin.y = -2; // je me déplace vers le bas
            System.out.println("le pseudo de robin est : " + robin.name + " et il se trouve en y =" + robin.y);
            System.out.println("le pseudo de l'inconnu est : " + inconnu.name + " et il se trouve en y =" + inconnu.y);
        }
    }
    

    Mes exemples sont toujours aussi nuls mais on va faire avec 😛
    Une nouvelle instance se déclare avec l'instruction new. Pour accéder aux variables qui se trouvent dans un objet il suffit de faire nomDeL'instance.nomDeLaVariable.
    Mettre le nom de la classe ne fonctionnera pas, par exemple si je mets Player.name il va me dire qu'il ne peut pas créer une référence statique d'un objet non statique et va me demander d'ajouter le mot clé statique devant la déclaration de la variable "name" dans la classe Player.
    En effet contrairement aux variables non statiques, une variable statique peut être accédée sans avoir l'instance de la classe dans laquelle elle se trouve car une variable statique n'a qu'une seule instance. Ainsi si on ajoute "static" devant "public String name;" name n'aura qu'une seule instance. En conséquence tous les joueurs auront le même nom, le dernier nom du dernier joueur déclaré sera utilisé pour tous les joueurs. Ce n'est pas vraiment le comportement attendu.
    Si je vous explique ça c'est parce que beaucoup de débutants ont tendance à cliquer sur ce que Eclipse propose lorsqu'il y a une erreur. Cependant Eclipse contairement à vous ne sait pas quel est le comportement que vous souhaitez obtenir. Et dans la plupart des cas la bonne solution n'est pas de passer la variable en statique mais d'utiliser l'instance de l'objet pour accéder à la valeur de la variable.

    Petite parenthèse, toujours concernant notre nom, avec l'instance du joueur on peut très bien modifier ce dernier de la même façon que je l'ai fait pour ma cordonnée y. Cependant il serait mieux que le nom du joueur ne change pas dans notre cas. Pour ça on peut ajouter le mot "final" devant la déclaration de notre variable (dans la classe Player) :

        public final String name;
    

    Ainsi la variable name ne pourra plus être modifiée après son initialisation dans le constructeur.

    L'héritage et les interfaces :

    Nous voulons maintenant créer un nouvelle objet qui sera un monstre. Par exemple un zombie. Tout comme notre joueur, ce zombie aura besoin de variables correspondantes à sa position. Du coup on aura dans notre joueur int x, int y, int z. Même chose dans notre zombie. Même chose pour tous autres montres ou animaux. C'est répétif tout ça non ? Pourquoi ne pas faire une classe commune ?
    C'est le principe de l'héritage.
    Nous allons créer une classe nommée Entity avec le contenu suivant :

    package fr.minecraftforgefrance.tutoriel;
    
    public class Entity
    {
        public int posX, posY, posZ; // en passant vous pouvez voir que l'on peut déclarer
                                                    // plusieurs variables du même type en une ligne de cette façon.
    
        public Entity(int x, int y, int z)
        {
            this.posX = x;
            this.posY = y;
            this.posZ = z;
        }
    }
    

    Maintenant pour faire en sorte que notre joueur hérite de la classe entity, nous allons ajouter extends après le nom de la classe :

    package fr.minecraftforgefrance.tutoriel;
    
    public class Player extends Entity
    {
        public String name;
        public Player(int x, int y, int z, String name)
        {
            super(x, y, z);
            this.name = name;
        }
    }
    

    Et idem pour notre zombie :

    package fr.minecraftforgefrance.tutoriel;
    
    public class Zombie extends Entity
    {
        public Zombie(int x, int y, int z)
        {
            super(x, y, z);
        }
    }
    

    Plus besoin de mettre les 3 variables coordonnées dans chaque classe, celles de la classe entité sont utilisées pour les deux classes filles (Zombie et Player).
    Comme vous pouvez le voir ici "super" est nécessaire. Cela permet d'appeler le constructeur de la classe mère (donc celle de Entity dans notre cas).
    Une classe peut hériter d'une classe qui elle même en hérite d'une autre (donc on peut créer une autre classe qui hérite de zombie, ou alors on aurait pu faire une classe EntityMonster qui hérite de Entity et faire en sorte que Zombie hérite de EntityMonster).
    Dans le modding l'héritage va très souvant être utilisé, quand on va ajouter un nouveau bloc, item, une nouvelle entité, etc … on ne va pas tout refaire de 0. On va simplement faire un "extends Block" ou "extends Item" ou encore "extends Entity", etc ... pour hériter de toutes les variables et fonction qui existe déjà dans les classes de Minecraft.

    Il est possible de déclarer une classe comme abstraite à l'aide du mot clé abstract. Une classe abstraite ne pourra pas être instanciée. On peut également ajouter dans une classe abstraite des méthodes abstraites. Les méthodes abstraites sont des méthodes vides qui devront être implémentées dans les classes filles.
    Par exemple créons une classe abstraite nommée action, contenant une fonction booléenne "est bien" :

    package fr.minecraftforgefrance.tutoriel;
    
    public abstract class Action
    {
        public abstract boolean isGood();
    }
    

    Maintenant créons deux classes qui héritent de cette dernière, par exemple aider les autres et spammer le forum. Eclipse va nous demander d'implémenter la méthode "isGood" :

    package fr.minecraftforgefrance.tutoriel;
    
    public class HelpOthers extends Action
    {
        @Override
        public boolean isGood()
        {
            return true; // aider les autres c'est bien, donc on retourne true :D
        }
    }
    
    package fr.minecraftforgefrance.tutoriel;
    
    public class SpamTheForum extends Action
    {
        @Override
        public boolean isGood()
        {
            return false; // spammer le forum c'est moins bien, donc retourne false.
        }
    }
    

    L'interêt est qu'on va pouvoir manipuler la valeur du retourne directement dans la classe Action. En moddant Minecraft vous allez souvent rencontrer des classes abstraites (les rendus des entités par exemple est une classe abstraite avec comme méthode abstraite getEntityTexture).

    Mais ce que vous allez le plus rencontrer ce sont les interfaces ! En effet si l'héritage est très pratique il y a des limites. La grosse limite de l'héritage en Java c'est l'impossibilité d'hériter de plusieurs classes. Pour une fois de plus faire le lien, dans Minecraft il y a les villageois (donc extends Entity), les entités de bloc (donc extends TileEntity) et les items (donc extends Item). Mais dans les trois cas on peut avoir un inventaire (les villagois ont l'inventaire de vente, le joueur lui aussi extends Entity a son propre inventaire, les coffres ont un inventaire et il est possible de faire un item ayant un inventaire)
    Comme on ne peut pas ajouter en plus un extends Inventory on utilisera implements IInventory (se lit i (i en anglais) inventory). Il n'est bien sûr pas possible d'implémenter n'importe quoi. Une classe normale ne peut pas être mise en implémentation. Seules les classes spécifiques du type interface peuvent être ajoutées. Par convension on ajoute un I majuscule en plus devant le nom de la classe pour la différencier. Un exemple d'interface :

    package fr.minecraftforgefrance.tutoriel;
    
    public interface IInventory
    {
        int getSize();
    
        void setItemInSlot(Object object, int slotIndex);
    
        Object getItemInSlot(int slotIndex);
    }
    

    (je n'avais pas d'idée donc j'ai repris le nom d'une classe existante dans Minecraft et j'ai mis 3 méthodes).
    Pour implémenter cette interface dans une classe il faudra ajouter implements IInventory après la déclaration de la classe. Il est possible d'implémenter plusieurs interface, il faut les séparer avec des virgules. Aussi, une interface peut hériter d'une autre interface.
    Tout comme dans une classe héritant d'une classe abstraite, les méthodes d'une interface devront être implémentées.

    Les énumérations :

    Les énumérations sont des classes spécifiques permettant de faire des listes d'objets constants. Il arrive souvent d'avoir besoin d'un type pour quelque chose. Par exemple une fonction qui va faire une action différente en fonction de l'os, l'os sera en argument de la fonction. On pourrait très bien utiliser un int et dire que 1 = Windows, 2 = Linux, 3 = OS X et 0 pour les autres :

    public void uneFonction(int os)
    {
       switch(os)
       {
          case 1:
          //action si l'os est Windows
          break;
          case 2:
          //action si l'os est Linux
          break;
    […]
    

    Cependant il y a plusieurs problèmes :

    • on pourrait mettre un nombre qui ne correspond à rien.
    • les chiffres 1, 2, 3 ne sont pas du tout explicite. Sans documentation ou sans lire tout le code impossible de savoir à quoi cela correspond.

    Pour régler ces deux problèmes on pourrait créer une classe contenant les différentes possibilités :

    package fr.minecraftforgefrance.tutoriel;
    
    public class OS
    {
        public static final OS WINDOWS = new OS();
        public static final OS LINUX = new OS();
        public static final OS OS_X = new OS();
        public static final OS OTHER = new OS();
    }
    

    et du coup utiliser cette classe pour notre fonction :

    public void uneFonction(OS os)
    {
        if(os.equals(OS.WINDOWS)
        {
            //action si l'os est Windows
        }
        else if(os.equals(OS.LINUX)
        {
            //action si l'os est Linux
        }
    […]
    

    Mais les énumérations restent bien plus pratiques !

    package fr.minecraftforgefrance.tutoriel;
    
    public enum EnumOS
    {
        WINDOWS,
        LINUX,
        OS_X,
        OTHER;
    }
    

    En plus d'être plus rapide à rédiger (une virgule pour séparer les valeurs, un point virgule après le dernier) il est possible d'utiliser la fonction values() (présent dans tout enum) pour avoir un tableau de toutes les valeurs (nous allons voir les tableaux dans la partie suivante).
    Il est également possible d'avoir un constructeur aux énumérations pour ajouter des variables à vos valeurs (comme pour n'importe quelle classe) :

    package fr.minecraftforgefrance.tutoriel;
    
    public enum EnumOS
    {
        WINDOWS("Windows"),
        LINUX("Linux"),
        OS_X("OS X"),
        OTHER("other");
    
        public String name;
        EnumOS(String name)
        {
            this.name = name;
        }
    }
    

    On pourra obtenir la valeur de cette variable en utilisant NomDeLEnum.NOM_DE_LA_CONSTANTE.variable. Exemple : String name = EnumOS.LINUX.name; retourne Linux

    Les énumérations sont très utilisées dans Minecraft (pour le type de créature, animaux ou monstre, le matériau des armures et des outils, la direction des blocs, etc …).

    Tableaux et listes :

    Les tableaux et les listes vont être très pratiques pour stocker plusieurs variables.
    Les tableaux ont une taille fixe et seule des variables du type indiqué pourront y être stockées.
    Voici quatre tableaux :

    int[] intArray = new int[8];
    int[] intArray2 = new int[]{7, 2, 8, 6, 4, 3, 8};
    String strArray[] = new String[5];
    String[] strArray1 = new String[]{"mff", "java", "forge"};
    

    On peut constater qu'il y a deux façons de déclarer un tableau. On peut mettre une ouverture de crochet et fermeture de crochet soit après le type de variable soit après le nom de variable. Dans les deux cas cela fonctionne. Ensuite pour initialiser le tableau il y a aussi deux possibilités. Soit on met "new TypeDeVariable[taille du tableau]" soit "new TypeDeVariable[]{valeur1, valeur2, etc …};
    Dans le premier cas les valeurs sont toutes 0 dans le cas d'un int, short, long, float ..., false pour une valeur booléenne et null pour les objets.
    Il est possible d'obtenir la taille d'un tableau avec la variable global length qui se trouve dans le tableau. On accède à la valeur d'un tableau en utilisant nomDuTableau[indice].
    Ainsi intArray2[0] vaut 7, intArray2[1] vaut 2, etc … (on commence toujours à 0 en informatique).
    On peut parcourir facilement les valeurs d'un tableau grâce à length :

    for(int i = 0; i < intArray2.length; i++)
    {
       System.out.println(intArray2*);
    }
    

    affichera 7 puis 2, puis 8, puis 6, etc ...
    On peut aussi effectuer une valeur au tableau de la même façon :

    intArray2[2] = 9;
    

    Le tableau vaut maintenant 7, 2, 9, 6, 4, 3, 8.
    Il existe aussi une deuxième syntaxe de la boucle for très pratique pour les tableaux et les listes :

    for(TypeDeVariable nom : nomDuTableau)
    

    Exemple :

    for(int value : intArray2)
    {
        System.out.println(value);
    }
    

    Revient au même que la boucle for vu juste avant.

    /!\ Attention lorsque vous manipulez des tableaux. Chercher un élement d'un tableau à un indice inexistant causera un OutOfBoundException.
    Par exemple, accéder à strArray1[-1] causera cette erreur (vrai pour n'importe quel autre tableau). Accéder à un indice trop élevé causera exactement le même problème. Dans mon cas strArray1 à 3 valeurs, (strArray1[0] = "mff", strArray1[1] = "java" et strArray1[2] = "forge"). Tenter d'accéder à strArray1[3] ou plus causera exactement la même erreur. N'oubliez jamais que le maximum d'un tableau est toujours égal à sa taille -1 (car on commence à 0). Accéder à tableau[tableau.length) causera toujours un OutOfBoundException.

    Les listes fonctionnent un peu comme des tableaux mais leur taille est variable.
    Il existe plusieurs types de listes en Java les deux principales sont les ArrayList et les LinkedList. Nous allons parler principalement des ArrayList car ce sont celles qui sont le plus utilisées dans Minecraft. Les LinkedList ont le désavantage d'être lentes quand il y a beaucoup de données (car chaque élément de la liste à une référence de l'élément suivant et du précèdent) mais ont l'avantage d'être rapides quand on manipule des objets en milieu de liste. Les ArrayList elles restent rapides même avec beaucoup d'éléments mais elles sont plus lentes pour manipuler les éléments en milieu de liste.
    Sachant que Minecraft a comme liste toutes les entités présentes dans le monde, celle de toutes les entités de bloc (donc des listes avec beaucoup d'élements) vous comprendrez pourquoi Mojang a choisis des ArrayList. (Je ne connais pas tout le code de Minecraft mais je n'ai encore jamais croisé de LinkedList dedans).
    De toute façon les LinkedList et les ArrayList s'utilise de la même façon :

            List<Integer> list = new ArrayList <Integer>();
            list.add(0);
            list.add(2);
            list.add(8);
    
            for(int i : list)
            {
                System.out.println(i);
            }
    

    Ici je crée une liste d'int, j'y ajoute 3 valeurs puis je les affiche avec une boucle for.
    Pour savoir quelle valeur se trouve à un indice, on utilisera list.get(indice). On peut aussi utiliser list.size() pour obtenir la taille de la liste. Et pour finir, on peut aussi utiliser list.remove(indice) pour retirer l'objet à l'indice indiquée ou utiliser list.remove(objet) pour retirer l'objet indiqué.

    Bon à savoir, une liste de x objets accepte aussi tous les objets qui héritent de x. Par exemple si je crée une liste de Entity (créé dans la partie objet et instance), je vais pouvoir mettre des instances de Zombie et de Player comme la classe de Zombie et Player héritent :

            List <Entity>entities = new ArrayList<Entity>();
            entities.add(new Zombie(0, 0, 0));
            entities.add(new Player(0, 0, 0, "robin4002"));
    

    En revanche dans la situation suivante on peut se poser la question : comment afficher le nom des joueurs ? Et oui en effet la liste contient des objets Entity, or le nom d'un joueur se trouve dans la classe Player. Pour ça on va devoir caster à l'objet entity Player. Seulement si on essaie de caster Player à Zombie cela ne va pas fonctionner :

            List<Entity> entities = new ArrayList<Entity>();
            entities.add(new Zombie(0, 0, 0));
            entities.add(new Player(0, 0, 0, "robin4002"));
    
            for(Entity entity : entities)
            {
                Player player = (Player)entity; // on cast Player
                System.out.println(player.name);
            }
    

    Ce code va causer un ClassCastException.
    Pour corriger ce problème on va utiliser instanceof :

            List<Entity> entities = new ArrayList<Entity>();
            entities.add(new Zombie(0, 0, 0));
            entities.add(new Player(0, 0, 0, "robin4002"));
    
            for(Entity entity : entities)
            {
                if(entity instanceof Player) // vérifie que entity est du type Player
                {
                    Player player = (Player)entity; // et donc le cast va forcément fonctionner
                    System.out.println(player.name);
                }
            }
    

    objet instanceof UneClasse retourne true si objet a comme UneClasse ou si sa classe hérite ou implémente UneClasse (fonctionne donc aussi avec les interfaces).
    Parler d'instanceof dans cette partie est un peu hors sujet, mais je l'ai mis ici car on utilise souvent instanceof avec des listes (mais vous l'utiliserez aussi ailleurs).

    Voila c'est la fin de ce long tutoriel. Je doute que vous ayez tout retenu, c'est pour ça que je vous recommande de garder ce tutoriel à portée et d'y revenir si vous avez un doute sur quelque chose qui concerne Java lorsque vous suivez les différents tutoriels de Modding.

    En vidéo

    Youtube Video

    Crédits

    Rédaction :

    Correction :

    Creative Commons
    Ce tutoriel de Minecraft Forge France est mis à disposition selon les termes de la licence Creative Commons Attribution - Pas d’Utilisation Commerciale - Partage dans les Mêmes Conditions 4.0 International

    retourRetour vers le sommaire des tutoriels


  • Modérateurs

    Pour compléter:

    Petite précision sur && et ||
    Ils évaluent la première partie et si elle est respectivement fausse ou vraie, l'autre n'est pas appellée ni vérifiée
    & et | vérifient les deux
    c'est pour ça qu'on peut faire sans avoir d'erreur ceci:

    if(item != null && item.count == 42)
    

    Avec Java 8 il est possible de définir des méthodes implémentées par défaut dans une interface



  • Bonjour je ne comprend pas pourquoi eclipse me renvois cette erreur : The type List is not generic; it cannot be parametrized with arguments <interger>```
    package fr.minecraftforgefrance.tutorial;

    import java.awt.List;
    import java.util.ArrayList;

    public class main {

    public static void main(String[] args) {
    System.out.println("Hello world");
    List <integer>list = new ArrayList <integer>();
    list.add(0);
    list.add(2);
    list.add(8);

    for(int i : list)
    {
    System.out.println(i);
    }
             }
    }


  • Administrateurs

    Salut,
    Il faut importer java.util.List et non java.awt.List