JavaRush /Java блог /Random /Кофе-брейк #98. Новое исключение Nullpointer в Java 17. Ч...

Кофе-брейк #98. Новое исключение Nullpointer в Java 17. Что означает <T> в Java?

Статья из группы Random

Новое исключение Nullpointer в Java 17

Источник: Dev.to Каждому Java-разработчику стоит знать о существовании в Java 17 нового исключения Nullpointer Exception или NPE. Это одна из таких ситуаций, которую всегда нужно стараться предотвратить. Иногда Nullpointer означает, что необходима отладка кода, чтобы найти небольшую ошибку. Кофе-брейк #98. Новое исключение Nullpointer в Java 17. Что означает &lt;T&gt; в Java? - 1NPE — это исключение времени выполнения, которое возникает, когда ваш код хочет использовать объект или ссылку на объект, имеющий нулевое значение. Оно может возникать, если не присвоено значение или объект не имеет ссылки. До последней версии OpenJDK (версия 17) обычное исключение Nullpointer в трассировке стека выглядело примерно так:

java.lang.NullPointerException: null
Конечно, это далеко не все, что вам нужно знать о трассировке стека. Как видите, тут не указано, где и почему возникло это исключение. Посмотрите, как с этой проблемой справляется Java 17:

Exception in thread "main" java.lang.NullPointerException:
Cannot assign field "i" because "a" is null
at Prog.main(Prog.java:5)
В этом примере исключение указывает, где и какова наша ссылка на нулевой объект. Вот так просто!

Что означает <T> в Java?

Источник: Dev.to <T> — это обычная буква, обозначающая “Type”, и она относится к концепции Generic в Java. Вы можете использовать для этого и другую букву, но, как можно заметить, буква T более предпочтительна. Кофе-брейк #98. Новое исключение Nullpointer в Java 17. Что означает &lt;T&gt; в Java? - 2

Что такое Generic?

Generic — это способ параметризации класса, метода или интерфейса. Давайте посмотрим на пример дженерика:

package Generics;

class House<T>{

    T doorNumber;

    public House(T doorNumber) {
        this.doorNumber = doorNumber;
    }

    public void print(){
        System.out.println("Your house number is: " + this.doorNumber);
    }
}
  • У нас есть класс под названием House, который может принимать произвольный тип объекта.
  • Далее у нас есть поле с именем doorNumber, которое также может принимать любой тип объекта.
  • В конце мы объявляем параметризованный конструктор и выводим номер двери.
Проще говоря, пользователь может решить, является ли этот номер Integer, String, Float, и так далее. ПРИМЕЧАНИЕ: вы можете использовать только Objects. Примитивные типы не поддерживаются, поскольку дженерики предназначены для использования во время компиляции. Когда мы используем Generics, T заменяет все, что может расширить класс Object, а у примитивов нет этого свойства. Допустим, мы хотим, чтобы doorNumber был строкой.

public class GenericsExample {
    public static void main(String[] args) {
        House<String> mainHouse = new House<>("14a");
        mainHouse.print();

    }
}
Результат получится таким:
Your house number is: 14a
Мы заменим букву “Т” на “String” и введем номер дома в конструктор. Мы можем использовать несколько типов, если, например, нам нужно, чтобы класс принимал более одного объекта. Можно добавить еще одну букву и тем самым сказать: мы хотим, чтобы класс принял другой Generic.

package Generics;

class House<T, V>{

    T doorNumber;
    V streetName;

    public House(T doorNumber, V streetName) {
        this.doorNumber = doorNumber;
        this.streetName = streetName;
    }

    public void print(){
        System.out.println("You live at: " + this.doorNumber + " " + this.streetName);
    }
}

public class GenericsExample {
    public static void main(String[] args) {
        House<Integer, String> mainHouse = new House<>(14, "Tellson Avenue");
        mainHouse.print();

    }
}
Результат будет таким:
You live at: 14 Tellson Avenue
До сих пор мы видели примеры использования Generic на уровне класса. Но у нас также могут быть общие методы и интерфейсы.

Метод Generic


package Generics;

class House{

    public <T> void print(T doorNumber){
        System.out.println("You live at house number: " + doorNumber);
    }

}

public class GenericsExample {
    public static void main(String[] args) {
        House mainHouse = new House();
        mainHouse.<Integer>print(14);

    }
}
Метод принимает любой тип объекта и выводит номер двери, который будет любым типом Object. В этом случае мы хотим, чтобы метод принимал целое число. Результат будет:
You live at house number: 14

Интерфейс Generic

Сначала создайте интерфейс.

package Generics;

interface Property<T>{
    void hasBalcony(T balcony);
}
Затем реализуйте интерфейс.

package Generics;

public class House implements Property<String> {

    @Override
    public void hasBalcony(String balcony) {
        System.out.println("Is there a balcony in the room? " + balcony);
    }

    public static void main(String[] args) {
        House mainHouse = new House();
        mainHouse.hasBalcony("YES");
    }

}
Результат:
Is there a balcony in the room? YES
Каковы преимущества использования дженериков?
  1. Лучшая проверка во время компиляции: если вы используете тип объекта, отличный от того, который вы указали, компилятор сообщит вам об этом.
  2. Возможность повторного использования: вы можете использовать класс, метод или интерфейс несколько раз, потому что вы решаете, какой тип объекта применять в зависимости от того, чего вы пытаетесь достичь.
  3. Он отлично подходит для структур данных и алгоритмов: ArrayList и HashMap — это лишь несколько примеров, где используется Generic.
Комментарии (3)
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ
Артём Пузыня Уровень 44
2 сентября 2022
public <T> void print(T doorNumber){ System.out.println("You live at house number: " + doorNumber); } public House(T doorNumber) { this.doorNumber = doorNumber; } не очень понятно зачем в первом методе <T> после слова public.
Yaroslav Golota Уровень 25
28 октября 2021
Почему название пекеджа с большой буквы?