Skip to content

Tour du langage Java

Nous allons faire un tour du langage Java sans forcément tout couvrir car il est très riche en fonctionnalités.

Warning

Comme le langage Java évolue apporte fréquemment des amélioration et simplifications, il se peut que les exemples de code vues ici soient différents de ce que vous trouvez dans la littérature.

Premiers pas

///usr/bin/env jbang "$0" "$@" ; exit $?

//DEPS com.google.guava:guava:32.1.3-jre

import static java.lang.System.*;

class Calculator {
  public int a;
  public int b;
  static double PI = 3.14;

  public double add() {
    return a + b + Calculator.PI;
  }

  static double multiply(int x, int y) {
    return x * y * PI;
  }
}

class Calculator2 {
  public int a;
  public int b;

  public int add() {
    return a + b;
  }
}

public class hello {

  public static void main(String... args) {
    out.println("Hello World");
    int i = 10;
    long j = 1_000_000;
    var message = "hello";
    message = "world";

    var c = new Calculator();
    c.add();

    Calculator.multiply(10, 1);
    Calculator.PI
  }
}

Classes, héritage et interfaces

///usr/bin/env jbang "$0" "$@" ; exit $?

import static java.lang.System.*;

interface Gamer {
  public void play();
}

interface HungryEater {
  public void eat();
}

class Human {
  private String name;

  public Human() {
    this("anonymous");
  }

  public Human(String name) {
    this.name = name;
  }

  public String getName() {
    return name;
  }

  public void setName(String name) {
    this.name = name;
  }
}

class HumanGamer extends Human implements Gamer {
  @Override
  public void play() {
    out.println("je joue");
  }
}

class HungryGamerHuman extends Human implements HungryEater, Gamer {

  @Override
  public void eat() {
  }

  @Override
  public void play() {
    // TODO Auto-generated method stub
    throw new UnsupportedOperationException("Unimplemented method 'play'");
  }

}

class Lion implements HungryEater {
  @Override
  public void eat() {
  }
}

class Student extends Human {
  private String idNumbeString;

  public Student(String name, String idNumbeString) {
    super(name);
    this.idNumbeString = idNumbeString;
  }

  public String getIdNumberString() {
    return idNumbeString;
  }

  public void setIdNumberString(String idNumbeString) {
    this.idNumbeString = idNumbeString;
  }
}

public class ClasseInterface {

  static void giveFood(HungryEater eater) {

  }

  static void runGame(Gamer gamer) {

  }

  public static void main(String... args) {
    out.println("Hello World");
  }
}

Types génériques

///usr/bin/env jbang "$0" "$@" ; exit $?

import static java.lang.System.*;
import java.util.*;

class IntegerCalculator {
  private Integer x;

  public IntegerCalculator(Integer x) {
    this.x = x;
  }

  Integer add(Integer b) {
    x += b;
    return x;
  }

  boolean isPositive() {
    return x >= 0;
  }
}

class GenericCalculator<Toto extends Number> {
  private Toto x;

  public GenericCalculator(Toto x) {
    this.x = x;
  }

  boolean isPositive() {
    return x.doubleValue() >= 0;
  }
}

public class GenericsDemo {

  public static void main(String... args) {
    var c1 = new GenericCalculator<Integer>(Integer.valueOf(10));

    List<String> items = new ArrayList<>();
    // Java déduit que le type du générique est "Integer"
    var integers = List.of(Integer.valueOf(19));
    System.out.println(integers);
  }
}

Collections

///usr/bin/env jbang --enable-preview "$0" "$@" ; exit $?

import static java.lang.System.*;
import java.util.*;
import java.util.Map.Entry;

public class CollectionDemo {

  public static void main(String... args) {
    // Type de base int
    int i = 99;
    // Objet Integer
    Integer j = 10;
    System.out.println(i);
    System.out.println("Integer: " + j.compareTo(i) + ", " + j.floatValue());
    int[] numbers = { 1, 20, 30 };
    List<Integer> items = new ArrayList<>();
    items.add(-3);
    items.add(11);
    items.add(22);
    var otherItems = List.of(-2, 11, 22);
    for (int k = 0; k < items.size(); k++) {
      System.out.print(items.get(k) + ", ");
    }
    System.out.println();
    Iterator<Integer> iter = items.iterator();
    System.out.println(iter.next() + ", " + iter.hasNext());
    System.out.println(iter.next() + ", " + iter.hasNext());
    System.out.println(iter.next() + ", " + iter.hasNext());
    System.out.println();
    System.out.println("Iter for loop");
    for (var iter2 = items.iterator(); iter2.hasNext();) {
      Integer value = iter2.next();
      System.out.print(value + ", ");
    }
    System.out.println();
    System.out.println("Iter for each");
    for (Integer item : items) {
      System.out.print(item + ", ");
    }
    System.out.println();

    System.out.println("Map");
    // 6786L => Litéral de type long (type de base)
    Map<String, Long> userIds = Map.of("Hugo", 6786L, "Rémy", 343L);
    System.out.println(userIds.get("Rémy"));
    Iterator<Entry<String, Long>> iterUserIds = userIds.entrySet().iterator();
    System.out.println(iterUserIds.next() + ", " + iterUserIds.hasNext());
    var entry = iterUserIds.next();
    System.out.println(entry.getKey() + "->" + entry.getValue() + ", " + iterUserIds.hasNext());
    for (var userIdEntry : userIds.entrySet()) {
      System.out.print(userIdEntry.getKey() + "->" + userIdEntry.getValue());
      System.out.print(", ");
    }
    System.out.println();
  }
}

Streams

///usr/bin/env jbang "$0" "$@" ; exit $?

import static java.lang.System.*;
import java.util.*;
import java.util.stream.*;

public class StreamDemo {

  public static void main(String... args) {
    out.println("Hello World");
    Stream<Integer> numbers = Stream.of(1, 2, 3, 4, 100);
    var filteredNumbers = numbers.filter((n) -> n % 2 == 0);
    var doubleNumbers = filteredNumbers.map((n) -> n * 2);
    var product = doubleNumbers.reduce(Integer::sum);
    System.out.println(product);

    var otherNumbers = Stream.of(1, 2, 3, 4, 100);
    var result = otherNumbers
        .filter((n) -> n % 2 == 0)
        .map((n) -> n * 2)
        .reduce(Integer::sum);

    System.out.println(result);

    var pairNumbers = Stream.of(1, 2, 3, 4, 100)
        .filter((n) -> n % 2 == 0).toList();

    var doublePairs = pairNumbers.stream().map((n) -> n * 2).toList();
  }
}

Interfaces fonctionnelles

///usr/bin/env jbang "$0" "$@" ; exit $?

import static java.lang.System.*;

@FunctionalInterface
interface MyCustomBiPredicate {
  boolean doSomething(int a, int b);
}

public class FunctionalInterfaceDemo {
  static boolean returnFalse(int a, int b) {
    return false;
  }

  static void callPredicate(MyCustomBiPredicate p) {
    System.out.println(p.doSomething(10, 0));
    System.out.println(p.doSomething(0, 0));
  }

  public static void main(String... args) {
    MyCustomBiPredicate p = (a, b) -> a > b;
    System.out.println(p.doSomething(10, 20));
    p = FunctionalInterfaceDemo::returnFalse;
    System.out.println(p.doSomething(111, 0));

    callPredicate(p);
    callPredicate(FunctionalInterfaceDemo::returnFalse);
    callPredicate((a, b) -> a > b);
  }
}

Equivalent en Kotlin:

fun doSomething(a: Int, b: Int) = a > b

class EntertainmentDevice(val name: String, var releaseYear: Int) {
  val isAfter2000: Boolean
    get() = releaseYear >= 2000
}

typealias MyCustomPredicate = (Int, Int) -> Boolean

fun main() {
  val p = ::doSomething
  println(p(10, 20))

  val p2: (Int, Int) -> Boolean = ::doSomething
  println(p2(10, 20))

  val p3: MyCustomPredicate = ::doSomething
}

Liste des interfaces fonctionnelles prédéfinies qui sont séparées en 4 catégories:

  • Consumer : Fonction qui prend des arguments génériques et ne renvoie rien (type de retour void)
  • Supplier : Fonction qui ne prend aucun argument et renvoie un valeur dont le type est générique
  • Function : Fonction qui peut prendre des arguments génériques et retourne une valeur générique.
  • Predicate : Fonction qui peut prendre des arguments génériques et retour un booléen. Un predicate peut être considéré comme un cas particulier d'une Function dont le type de retour est un booléen.

La convention est de rajouter le terme bi pour les fonctions avec deux arguments (comme BiPredicate). Voici des exemples de définition de quelques interfaces fonctionnelles:

@FunctionalInterface
public interface Consumer<T> {
    void accept(T t);
}

@FunctionalInterface
public interface Supplier<T> {
    T get();
}

package java.util.function;
@FunctionalInterface
public interface Function<T, R> {
    R apply(T t);
}

@FunctionalInterface
public interface BiFunction<T, U, R> {
    R apply(T t, U u);
}

@FunctionalInterface
public interface Predicate<T> {
    boolean test(T t);
}

Null safety

Définition: la null safety est toute fonctionnalité qui permet de ne plus avoir de null pointer exception à l'exécution.

Java propose deux possibilités pour aovir une sorte de null safety qui ne sont moins puissantes que ce que l'on peut trouver dans d'autres langages comme Kotlin, Swift ou TypeScript par exemple.

Type optionnel
///usr/bin/env jbang "$0" "$@" ; exit $?

// Optional<T> permet d'englober une valeur et fournit des méthodes pour la récupérer si elle est présente
// - Attention: l'objet Optionnel en lui-même peut être null
// - En plus, on peut récupérer la valeur contenue même si elle est null et on aura une exception autre que la NPE

import static java.lang.System.*;
import java.util.*;

public class OptionalDemo {

  static String getFromInternet() {
    return "dsfsdfdsf";
  }

  static Optional<String> getFromInternetOpt() {
    return Optional.of("dsfsdfdsf");
  }

  public static void main(String... args) {
    out.println("Hello World");

    Optional<String> myOptionalText = Optional.empty();
    if (myOptionalText.isPresent()) {
      System.out.println(myOptionalText.get());
    }
    var optioanlValue = getFromInternetOpt();
    if (optioanlValue.isPresent()) {
      System.out.println(optioanlValue.get());
    }
  }
}
Annotations null
/*
 * This source file was generated by the Gradle 'init' task
 */
package org.example;

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

import javax.annotation.Nonnull;
import javax.annotation.Nullable;

// Les annotations de nullabilité sont traitées par certains IDE et outils pour s'assurer avant la compilation qu'il n'y aura pas de NPE
// Les IDE génèrent généralement un avertissement et non une erreur
// Ces annotations sont ignorées par le compilateur Java (au moins jusqu'à la version 21)
// Il y a aussi une multitude d'annotations fournies par différentes librairies qui peuvent créer de la confusion

public class App {

  @Nonnull
  private List<String> items;

  @Nonnull
  List<String> getItems() {
    return this.items;
  }

  public App() {
    this.items = new ArrayList<>();
  }

  @Nonnull
  public String getGreeting() {
    return "dsfsdfd";
  }

  @Nonnull
  public String getValue(@Nullable String value) {
    if (value != null) {
      return value.toUpperCase();
    }
    return "";
  }

  public static void main(String[] args) {
    @Nonnull
    App app = new App();

    app.getValue(null);

    System.out.println(app.getItems().size());
    app.getItems().add(null);
    app.getItems().add("toto");
    System.out.println(app.getItems().size());
  }
}