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 внутри тела метода, если объявление не будет сделано?