How to Make the Return Type of a Method Generic

How do I make the method return type generic?

You could define callFriend this way:

public <T extends Animal> T callFriend(String name, Class<T> type) {
return type.cast(friends.get(name));
}

Then call it as such:

jerry.callFriend("spike", Dog.class).bark();
jerry.callFriend("quacker", Duck.class).quack();

This code has the benefit of not generating any compiler warnings. Of course this is really just an updated version of casting from the pre-generic days and doesn't add any additional safety.

How to create a method with a generic return type in dart

You need to write two different functions.

When calling a generic function, like T parseProperty<T>(String text) { ... }, you need to know which type to return at compile time. Otherwise you'll have to pass dynamic as type argument.

As you have also noticed, simply abstracting over the return type makes it hard to implement the function. You'll have to put as T on every return expression, because there is no way to invent an object of an unknown type.

All in all, you are better off having either two functions, if you know the type at compile-time, or having one function returning Object if you don't.

Generic return type from a generic method

You can't do this in a type safe way without modifying the downstream code (context.get()) because the generic type is erased at run time. In fact, currently the clazz argument isn't being used at all: you could simply delete that argument and the method would perform identically since the cast to (T) is currently a no-op due to erasure. Your current code is not type safe even for non-genric classes.

So if you don't care about the safety, and just want to avoid having casts in the client code, you can just delete the last argument, and a suppress warnings once on the method.

If you want type safety, you can use super type tokens to keep the generic type information: Guava's TypeToken works and is easily available (IMO every Java project should use Guava already).

It will require downstream changes to your code, however - since you need to capture the type information when objects are added and check it when they come out. You didn't share the code for your Context class, so it's hard to say if that's possible, but in any case you'd want something like this:

static class TypedParam {
final TypeToken<?> type;
final Object object;
private TypedParam(TypeToken<?> type, Object object) {
this.type = type;
this.object = object;
}
}

public static class Context {
Map<String, TypedParam> params = new HashMap<>();
TypedParam get(String name) {
return params.get(name);
}
}

public static <T> T getParameter(final Context context, final String name, final TypeToken<T> type) {
TypedParam param = context.get(name);
if (type.isAssignableFrom(param.type)) {
@SuppressWarnings("unchecked")
T ret = (T)param.object;
return ret;
} else {
throw new ClassCastException(param.type + " cannot be assigned to " + type);
}
}

Basically you know the generic type of all the objects in your parameter map, and check with a "generic aware cast" at run time. I didn't include Context.put() above, but you'd need to capture the type info when the parameter was added.

You still have a @SuppressWarnings("unchecked"), but here it's provably type-safe since you are maintaining the type information (in the guts of many generic classes you'll find provably safe casts, so they can't always be avoided even in correct code).

In Typescript how to get the generic return type of a method on a class instantiated in a factory(ish) function

This answer was inspired by @jcalz's suggestion in the comments. I wasn't able to make it work using infer, as he suggested, but his suggestion using ReturnType works as intended and this is what I use here.

interface Foo<T> {
foo(): T;
}

class Bar implements Foo<string> {
foo(): string {
return 'Bar';
}
}

class Baz implements Foo<number> {
foo(): number {
return 43;
}
}

class FooRef<T extends Foo<U>, U = ReturnType<T["foo"]>> {
constructor(public instance: T) { }

doIt(): U {
return this.instance.foo();
}
}

function fooBar<T extends Foo<any>>(foo: new (...args: any[]) => T) {
return new FooRef(new foo());
}

const barRef = fooBar(Bar);
barRef.instance // (property) FooRef<Bar, string>.instance: Bar
console.log(barRef.doIt().toUpperCase()); // "BAR"

const bazRef = fooBar(Baz);
bazRef.instance // (property) FooRef<Baz, number>.instance: Baz
console.log(bazRef.doIt().toFixed(2)) // "43.00"

Return type of generic function

So, I'm not sure this will also cover the things you need for production. But the return type looks alright like this:

interface IResponse<T> {
result: T;
}

type FnType<P, R> = (params: P) => R;

const r1: IResponse<string[]> = { result: ['123'] };
const r2: IResponse<number[]> = { result: [456] };

const myResults = <P,R>(fn: FnType<P,IResponse<R[]>>, params: P): R[] => {
return [
...fn(params).result,
...fn(params).result
];
}

const getValue = <T>(r: IResponse<T>) => r;

myResults(getValue, r1);

myResults(getValue, r2);

Playground link

Edit:
I'm actually not exactly sure why the code in question does not give correct IntelliSense, it makes sense to me.

But what I can see is that you used three generic types T, R and P. My Idea is that basically you only have 2 types to worry about which are params and the Result of the method.

I basically say, whatever type is inside the getValue's IResponse will be the final output of the myResults function.

Why is does the generic R go before the return type?

A class can be generic or not, but whatever your class is public class MyClass or public class MyClass<E> it might happen that you would like to offer a method that uses a generic parameter for itself only, that no other methods in your class are using.

So the <R> here :

public <R> Stack<R> map(Function<E, R> mapper) {

}

means : know that there is a specific parameter I will use here, but for that method only.

it could be specified the same way you would do in a class declaration :

public <R extends Serializable> Stack<R> map(Function<E, R> mapper) {

}

for example.


In your case, this parameter is used to link the type you return to the type you give in input of this method : it ensures that if you give a mapper of type Function<E, R>, you will receive an R object in return.


Another convenient use of these "extra" generic parameters on methods is the dynamic checking of the type of an object returned. I use it often :

public <T> T myMethod(Class<T> expectedClass, int a, int b) {
...
// My result comes in an object o.
Object o;

if (expectedClass.isInstance(o) == false) {
// ClassCastException
}

return (T)o; // You are sure it has the expected type.
}

Method call with Generic return type in Java

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.



Related Topics



Leave a reply



Submit