Java Generics и Static Factory Методы - Синтаксис

Вот что у меня есть:

public class Node<T> {

    // instance variables
    private Node<T> next;
    private T data;

    // construct with data
    private Node(T data){
        next = null;
        this.data = data;
    }

    // construct without data
    private Node(){
        next = null;
        this.data = null;
    }

    // static factory method
    public static <T> Node<T> newNodeWithData(T data){
        return new Node<T>(data);
    }

    // static factory method
    public static <T> Node<T> newNode(){
        return new Node<T>();
    }
...
}

Мой вопрос действительно о синтаксисе дженериков в сочетании с методом статического factory. Я действительно не понимаю, почему мы помещаем <T> перед типом возвращаемого значения в объявлении метода. Это похоже на приведение типов? Любая помощь будет высоко оценена!

Ответы

Ответ 1

То, о чем вы спрашиваете, это тип inferrence.

Поскольку это статический метод, он должен где-то выводить тип Generic; У вас нет экземпляра класса. То, что означает <T>.

В случае вашего метода, который не принимает аргументов, он фактически выводит его из целевого объекта назначения. Например, предположим, что ваш метод выглядит следующим образом:

public static <T> List<T> getNewList() {
    return new ArrayList<T>();
}

При использовании этого метода T выводится из цели (в данном случае String):

List<String> myList = MyClass.getNewList();

В другом статическом методе, где у вас есть общий аргумент, T выводится из передаваемого типа:

public static <T> List<T> getNewListWithElement(T element) {
    List<T> list = new ArrayList<T>();
    list.add(element);
    return list;
}

Здесь, если вы попытались сделать:

List<String> myList = MyClass.getNewListWithElement(new Integer(4));

Это скажет вам, что ваш тип цели был неправильным, и вам понадобился List<Integer>

В частности, это описано в разделах 15.12.2.7 и 15.12.2.8 JLS.

Ответ 2

Причина, по которой вы должны украсить статический метод таким сахаром, состоит в том, что в качестве статического метода он не наследует T из объявления класса.

Вы могли бы так же хорошо:

// static factory methods
public static <Q> Node<Q> newNode(){
    return new Node<Q>();
}

public static Node<String> newStringNode(String s){
    return new Node<String>(s);
}

Простой описатель декларации может помочь:

// This static method would have a <T> parameter to the class if it was not static
public static <T> 
// It returns an object of type `Node` with generic parameter T
Node<T> newNode(){
    // And here it is doing it business.
    return new Node<T>();
}

Ответ 3

Это единственный способ параметризовать статический метод, поскольку исходный T в объявлении Node привязан к полям экземпляра и методам Node. Поэтому вы можете написать:

public static <T1> Node<T1> newNode(){
    return new Node<T1>();
}

Оригинальный T привязан к экземпляру класса Node и не может ссылаться в статическом контексте. Это приведет к ошибке компиляции:

// ERROR
public static Node<T> newNode(){
    return new Node<T>();
}

Ответ 4

<T> - это просто сигнал, что этот метод использует T как переменную типа. Без него компилятор подумает, что T - это class, interface или enum, который объявлен где-то и выводит ошибку. Это не то же самое значение T, которое используется в вашей первой строке. Вы можете заменить T в этом методе любой другой буквой, возможно, это поможет понять.

Ответ 5

T выводится из параметра

public static <T> List<T> getNewListWithElement(T element)

Как компилятор может сделать разницу между T как классом и T как общим аргументом? Решение состоит в том, чтобы использовать для указания элемента T, является общим, а не классом/интерфейсом.

T выведен из использования

public static <T1> Node<T1> newNode(){
    return new Node<T1>();
}

Кто будет T1 внутри тела метода, если объявление не будет сделано?