Method call with Generic return type in Java

30,456

Solution 1

It's perfectly fine to declare a method with the signature public <T> T getDate().

However, it is impossible to implement the method that returns what you want. What a method does at runtime cannot depend on its type parameter alone, because it doesn't know its type parameter.

To get an intuition for this, realize that any code written with generics can also be written equivalently without using generics, by simply removing generic parameters and inserting casts where appropriate. This is what "type erasure" means.

Therefore, to see whether your method would be possible in Generics, simply ask, how would you do it without Generics:

public Object getDate() 
{
    // what would you do here?
}

Date myDate = (Date)getDate();

If you can't do it without Generics, you cannot do it with Generics either.

C++ templates are completely different. For templated functions and classes, C++ templates generate a ''separate copy'' of the function or class for each type argument that is used with it. i.e. the compiler takes the templated code and "copy and pastes" it into multiple versions, each separate. Therefore, each copy of the code is specific to a certain type argument, and can thus use that type at runtime.

This is why C++ templated code is required to be available in source form in order for you to use it -- there is no such thing as "compiled" templates. However, in Java, a compiled generic class can be used. Generic classes and methods in Java do not assume anything about the types they can be used on.

Solution 2

Okay, now with the edit to make it clear that the code is conditional on T...

No, there's nothing simple within Java to make this work - due to type erasure, as your question mentions. You can pass in Class<T>:

public <T> T getDate(Class<T> clazz)
{
    // Now use clazz to work out what to do
}

... but you can't do anything which depends on the "value" of T itself at execution time, as that's simply not known. Generics is somewhat anaemic in Java, unfortunately :(


EDIT: Before the edit to make the code conditional on T...

It's just a matter of specifying the type argument differently:

import java.util.Date;

class Test {

    public static <T> T getDate() {
        return (T) new Date();
    }

    public <T> T getDateInstanceMethod() {
        return (T) new Date();
    }

    public static void main (String [] args) {
        Date date = Test.<Date>getDate();

        // Compiles fine, but is obviously bogus.
        String string = Test.<String>getDate();

        // And specifying a type argument for an instance method
        Test test = new Test();
        date = test.<Date>getDate();
    }
}

I've always preferred the "put the type arguments just before the regular arguments" approach too, but there we go...

EDIT: As rgettman points out, type inference will do the right thing for you anyway here, so you don't actually need to specify the type arguments in many cases. Sometimes you do though.

Solution 3

With Arrays.asList, sometimes it's necessary to specify the return type. Java attempts to "narrow" the return type based on the common supertype(s) of all the arguments, but sometimes you need a specific type. For example,

List<Number> list = Arrays.asList(1, 2, 3);

Here, Arrays.asList returns a List<Integer> and you get a compiler error. To get it to return a List<Number>, you must specify the generic type.

List<Number> list = Arrays.<Number>asList(1, 2, 3);
Share:
30,456
Jason
Author by

Jason

Updated on July 09, 2022

Comments

  • Jason
    Jason almost 2 years

    In C++ you can specify the return type of a function in a parameter, such as:

    C++

        float* myFloat = CollectionClass.ptr<float>();
        int*   myInt   = CollectionClass.ptr<int>();
    

    Is there an equivalent in Java to specify the return type without adding additional class args?

    Java

        //This doesn't work (due to type erasure) 
        public <T> T getDate() 
        {
            if (T instanceof Date)
                return new Date();
            if (T instanceof Calendar)
                return new Calendar();
        }
    
        Date myDate = getDate<Date>();
    
  • T.J. Crowder
    T.J. Crowder about 11 years
    Probably worth mentioning that you can sometimes allow the compiler to infer it from the target, as well, so Date date = Test.getDate(); actually works just fine in your code above.
  • amphibient
    amphibient about 11 years
    what would be the purpose of doing it this way that you couldn't do if getDate simply returned Date? what does the use of generics buy you in this case?
  • T.J. Crowder
    T.J. Crowder about 11 years
    @foampile: More of a question for Jason than Jon, isn't it? (But Jason's update to the question clarifies things...)
  • Jon Skeet
    Jon Skeet about 11 years
    @T.J.Crowder: Good point. Will edit - especially with the bit about inference.