Введение в программирование

Улучшения языка

Функциональные интерфейсы

Лямбда-выражения

  • Реализация функционального интерфейса
    • R BiFunction<T, U, R>.apply(t, u) – бинарная функция
    • BiFunction<String, Long, String> f =
          (String s, Long v) -> {
              return s + v;
          }
      
    • BiFunction<String, Long, String> f =
          (String s, Long v) -> s + v;
      
    • BiFunction<String, Long, String> f = (s, v) -> s + v;
      
    • Function<String, String> f = s -> s + s;
      
    • Supplier<String> s = () -> "!";
      
    • Consumer<String> c = s -> System.out.println(s + s);
      

Ссылки на методы

  • Метод класса
    • Function<String, Integer> f1 = Integer::parseInt;
      // x -> Integer.parseInt(x);
      
  • Метод экземпляра
    • Function<Integer, String> f2 = Integer::toString;
      // x -> x.toString();
      
  • Метод экземпляра объекта
    • Integer i = 2; // Не обязательно final
      Supplier<String> f3 = i::toString; // () -> i.toString();
      
  • Конструктор
    • Function<String, Integer> f = Integer::new;
      // s -> new Integer(s);
      

Замыкания

  • Effectively final переменные
    • Ровно одно присваивание
    • Может быть без модификатора final
    • Могут использоваться в лямбда-выражениях
  • Замыкание → объект в куче
    • Разные вызовы → разные объекты
  • Пример
    • /* final */ String hello = "Hello, ";
      Function<String, String> greeter = 
              name -> hello + name;
      

Ограничения лямбда-выражений

  • Захват не-final переменных
  • Прозрачный проброс исключений
    • // Не работает, так как write бросает IOException
      Consumer<String> c = writer::write;
      
  • Изменение потока управления
    • collection.forEach(e -> {
          if (e.equals("done")) {
              // Что делать?
          }
      });
      
  • Реализация
    • Лямбды — не классы

Использование var

  • Локальная переменная с инициализатором
    • Тип — тип инициализатора
  • Примеры
    • var hello = "world";
      
    • var counters = new HashMap<String, Integer>();
      

Некорректное использование

  • Без инициализатора
    var hello;
    
  • Инициализация null
    var hello = null;
    
  • Смена типа при присваивании
    var counters = new HashMap<String, Integer>();
    counters = Map.copyOf(counters);
    

Запуск без компиляции

  • Весь код в одном файле
    • java HelloWorld.java
  • Зависимости
    • java --class-path=.. HelloWorld.java
  • Shebang
    • HelloWorld.j *(не .java)
      #!/usr/bin/java --source 11
      public class HelloWorld {
          public static void main(String[] args) {
              System.out.println("Hello, World");
          }
      }
      

Switch без «проваливания»

  • Что выводит?
    int n = ...;
    switch (n) {
        case 0 -> System.out.println("zero");
        case 1 -> System.out.println("one");
        case 2 -> System.out.println("two");
        default -> {
            System.out.println("too many");
            System.out.println("many");
        }
    }
    
  • Одно из сообщений

Switch как выражение

  • Пример
    int n = ...;
    System.out.println(switch (n) {
        case 0 -> "zero";
        case 1 -> "one";
        case 2 -> "two";
        default -> {
            System.err.println("to");
            yield "many";
        }
    });
    

Текстовые блоки

  • Было
    String code = "function hi() {\n" +
            "console.log('\"Hello, world!\"');\n" +
            "}\n" +
            "\n" +
            "hi();\n";
    
  • Стало
    String code = """
                  function hi() {
                      console.log('"Hello, world!"');
                  }
                                  
                  hi();
                  """;
    

Escape-последовательности

  • Обычные
    • \n, \r, \", \', ...
  • Переводы строк
    • // helloworld
      String hello = """
              hello\
              world
              """;
      
  • Значащие пробелы s
    • // hello world
      String hello = """
              hello\s\
              world""";
      

Интерпретация

  • Нормализация переводов строк
    • LF (\n)
  • Удаление концевых пробелов
    • Кроме \s
  • Удаление общего префикса из пробелов
    • Пустые строки игнорируются
    • Специальная обработка закрывающего """

Запись

  • Неизменяемый тип-значение
  • Пример
    record Point(int x, int y) {}
    
    Point p = new Point(10, 20);
    System.out.println(p.x());
    // True
    System.out.println(p.equals(new Point(10, 20));
    

Части записей

  • Компоненты
    • Тип
    • Имя
  • Генерируются
    • Канонический конструктор
    • private final поля
    • public методы доступа
    • equals, hashCode, toString

Канонический конструктор

  • По умолчанию
  • Полное переопределение
    • record Range(int lo, int hi) {
          Range(int lo, int hi) {
              assert lo < hi : "Invalid range";
              this.lo = lo;
              this.hi = hi;
          }
      }
      
  • Частичное переопределение
    • record Range(int lo, int hi) {
          Range {
              assert lo < hi : "Invalid range";
          }
      }
      

Правила

  • Предок не указывается
  • Являются final
  • Не имеет других полей
    • Можно static
  • Сериализуемы
  • Могут реализовывать интерфейсы
  • Дополнитлеьные методы и конструкторы

Пример

  • class Point {
        int x, y;
        public boolean equals(Object obj) {
            if (obj instanceof Point that) {
                return this.x == that.x && this.y == that.y;
            }
            return false;
        }
    }
    
  • Что необычно?
    • Не требуется
      Point that = (Point) obj;
      

Общий случай

  • Синтаксис
    if (объект instanceof Тип имя && /*...+*/) {
        /*...+*/
    } else {
        /*...−*/
    }
    
    if (!(объект instanceof Тип имя) || /*...+*/) {
        /*...−*/
    } else {
        /*...+*/
    }
    

Для записей (21+)

  • Простые
    if (obj instanceof Point(int x, int y)) {
        return this.x == x && this.y == y;
    }
    return false;
    
  • Вложенные
    if (
        obj instanceof 
            Rect(Point(int x1, int y1), Point p2)
    ) {
        System.out.format("(%d, %d) %s", x1, y1, p2));
    }
    

В switch (21+)

  • Пример
    static String format(Object obj) {
        return switch (obj) {
            case Integer i -> String.format("int %d", i);
            case Double d -> String.format("double %f", d);
         // When
            case Long l when l > 0 -> String.format("+long %d", l);
            case Long l when l < 0 -> String.format("-long %d", l);
         // Record
            case Point(int x, int y) -> 
                String.format("Point(%d, %d)", x, y);
            case null -> null;
            default -> obj.toString();
        };
    }
    

Sealed Class

  • Класс с контролируемыми наследниками
    • Должны быть известны во время компиляции
    • В одном пакете (модуле)
  • Явное разрешение
    sealed interface Shape 
        permits Rect, Circle, Line {
    
  • Вложенные в базовый класс
    sealed class Inline {
        final class Emphasis extends Inline {}
        final class Strong      extends Inline {}
        final class Text          extends Inline {}
    }
    

Контроль наследников

  • Только прямые наследники
  • Рекурсивный контроль
    • final            — нет наследников
    • sealed        — контролируемые наследники
    • non-sealed — произвольные наследники

Пропущенные темы

  • Generic-классы
    • Второй семестр
  • Аннотации
    • Второй курс
  • Модули
    • Второй курс
  • Многопоточность
    • Второй курс
  • Сериализация
    • Второй курс

Ссылки

Вопросы

???