How to Make the Method Return Type 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.

Make method return type generic

Well your code works perfectly, and Manager.builder().age(25).build() actually returns a Manager. It is only a compile time problem.

The following Junit test should succeed (it does on my tests):

@Test
public void testEss3() throws Exception {
Person emp = Manager.builder().age(25).build();
assertTrue(emp instanceof Manager);
}

In fact, it looks like as you declare no variable to host the builder, and as the method age is not defined in Manager.Builder nor in its direct subclass Employee.Builder, the Java compiler assume that it will return an object of the class in which it is declared, that is a Person.Builder. It is not false because it is actually an ancestor class. But from that point, the compiler do not know the exact class returned by build() and only knows that it will be a Person.

But the following code is accepter by the compiler:

    Manager.Builder<Manager,?> builder = Manager.builder();
Manager emp = builder.age(25).build();

How to return different types in a method using generics?

In generic methods, the type is inferred by the compiler using the actual arguments. But your argument doesn't have any type T. You can send another parameter Class<T> to tell the type of the return value:

public <T extends Root> T gets(int type, Class<T> c) {
switch (type) {
case 1:
return c.cast(new A());
case 2:
return c.cast(new B());
default:
throw new RuntimeException();
}
}

But in your case you don't know the type before you call this method.

So all you can do is make this method non-generic and change return type to just Root. Then you can do instanceof check to identify the class, perform casting, and then get the relevant fields.

It doesn't make sense to make this method generic anyway, because generic methods can work with different type of supplied data. But in your case it's always an int. So I would say your method is not really generic.

Return Type of Java Generic Methods

This question suits one of my old notes. I hope this illustration helps:

Sample Image
Sample Image

TypeScript Type Get Return Type of Constrained Function Type Generics

Short answer, there is an PR merged for TS4.7 for this feature. Of particular relevance to your question...

type Box<T> = ReturnType<typeof makeBox<T>>;  // { value: T }

This is super close to a solution of #37181. Will it will allow us to do...

return { value }; };

// Can we do it or something similar? Currently doesn't compile :(
type MakeBox<T> = ReturnType<typeof makeBox<T>>

// As it now allows us to do (no generics though)
const stringMakeBox = makeBox<string>; type MakeBox = ReturnType<typeof stringMakeBox>

// And even more relevant to support generics if we can now do: type
MakeBox = ReturnType<typeof makeBox<string>>

Yes, you can indeed use that pattern to capture a generic return type of a generic function. This is something that previously wasn't possible. I'll update the PR description to include an example.

Comment here

I can't find anything on whether this works on type aliases for functions, or just works on runtime functions only (inferred with typeof), but you can possibly just use the actual runtime implementations and use typeof as needed

Here is this working on the nightly build, note that you'll still need a runtime implementation, whether or not it actually implements anything is up to you, it could be some mock function that doesn't return anything

type FooFormatter = <S extends string>(input: S) => `foo-${S}`;

const FooFormatterImplementation: FooFormatter = {} as any; //mock implementation

type StringFormatter = <S extends string>(input: S, ...args: never) => string;

type foo = ReturnType<typeof FooFormatterImplementation<"hey">>
// ^? `type foo = "foo-hey"`

My two-cents and more (More Post Script stuff)

I highly recommend investigating some of the types they have over at type-fest, if you haven't already. Especially

  • Split
  • Or in general all their Change case utilities,

Reading through the library, it looks like you've got a good sense of types, and I think you may actually be limited by the TS version of Deno.

There are a bunch of workarounds, but I'm not sure if they're really applicable in your usecase.

  • https://stackoverflow.com/a/50006640/17954209
  • https://stackoverflow.com/a/62620115/17954209

Edit: Workaround with declaration merging and interfaces

This is not dependent upon TS4.7 Hooray!

type FooFormatter = <S extends string>(input: S) => `foo-${S}`;

type BarFormatter = <S extends string>(input: S) => `bar-${S}`

export interface Formatters {
FooFormatter: FooFormatter,
BarFormatter: BarFormatter
}
// then anyone wanting to add a custom formatter, has to modify and reexport the interface,
// this is similiarly done in @react-mui, using module augmentation
// https://www.typescriptlang.org/docs/handbook/declaration-merging.html

function takeInnerTest<T extends string, FmtKey extends keyof Formatters>(input: T, formatter: FmtKey)
{
type formattersDiscriminated = {
[K in keyof Formatters]: Formatters[K]
}[FmtKey]
const __mockFunction: formattersDiscriminated = ((...args: any[]) => undefined) as any
const __mockReturn = __mockFunction(input)
type String = ReturnType<typeof __mockFunction>
type ReturnValue = Extract<typeof __mockReturn, String>
return null! as ReturnValue
}

const foo3 = takeInnerTest("hey", "FooFormatter")
type foo3 = typeof foo3
// ^?
const foo4 = takeInnerTest("hey", "BarFormatter")
type foo4 = typeof foo4
// ^?

TS Playground

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).

How do I make the return type of a method generic?

You need to make it a generic method, like this:

public static T ConfigSetting<T>(string settingName)
{
return /* code to convert the setting to T... */
}

But the caller will have to specify the type they expect. You could then potentially use Convert.ChangeType, assuming that all the relevant types are supported:

public static T ConfigSetting<T>(string settingName)
{
object value = ConfigurationManager.AppSettings[settingName];
return (T) Convert.ChangeType(value, typeof(T));
}

I'm not entirely convinced that all this is a good idea, mind you...



Related Topics



Leave a reply



Submit