Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Method call with Generic return type in Java

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>();
like image 949
Jason Avatar asked Feb 27 '13 17:02

Jason


2 Answers

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.

like image 51
Jon Skeet Avatar answered Sep 25 '22 20:09

Jon Skeet


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.

like image 37
newacct Avatar answered Sep 22 '22 20:09

newacct