简体   繁体   中英

Java Generics and Static Factory Methods — Syntax

Here's what I've got:

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>();
    }
...
}

My question really just about the syntax of generics coupled with that of a static factory method. I don't really understand why we put the < T > before the return type in the method declaration. Is it kind of like typecasting? Any help would be much appreciated!

What you're asking about is type inferrence .

Since it's a static method it has to infer the Generic type from somewhere; You don't have an instance of the class. That's what the <T> means.

In the case of your method that takes no arguments it's actually inferring it from the target of the assignment. For example, say your method looked like this:

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

When using this method, T is inferred from the target (in this case String ):

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

In your other static method where you have a Generic argument, T is being inferred from the type being passed in:

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

Here, if you tried doing:

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

It would tell you that your target type was wrong, and you needed a List<Integer>

Specifically this is covered in sections 15.12.2.7 and 15.12.2.8 of the JLS.

The reason you must decorate the static method with such sugar is because, as a static method, it does not inherit the T from the declaration of the class.

You could just as well do:

// 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);
}

A simple narrative to the declaration may assist:

// 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's business.
    return new Node<T>();
}

This is an only way to parametrize a static method, as the original T in the Node declaration is bound to instance fields and methods of the Node. So you could write:

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

The original T is bound to an instance of Node class and cannot be referenced in static context. This would result in a compilation error:

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

The <T> is just the signal, that this method uses T as type variable. Without it, the compiler would think, T is a class , interface or enum that is declared somewhere and output an error. It is not the same T as used in your first line. You can replace the T in this method with any other letter, maybe that helps understanding.

T inferred from parameter

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

How can the compiler make the difference between T as a class and T as a generic argument? The solution is to use to specify T element is a generic and not a class/interface.

T inferred from usage

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

Who will be T1 inside the method body if no declaration would be made?

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM