Java Generics: Generic Type Defined as Return Type Only

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.

Java Generics: Generic type defined as return type only

The method returns a type of whatever you expect it to be (<X> is defined in the method and is absolutely unbounded).

This is very, very dangerous as no provision is made that the return type actually matches the returned value.

The only advantage this has is that you don't have to cast the return value of such generic lookup methods that can return any type.

I'd say: use such constructs with care, because you lose pretty much all type-safety and gain only that you don't have to write an explicit cast at each call to get().

And yes: this pretty much is black magic that blows up at runtime and breaks the entire idea of what generics should achieve.

Java Generics: Returning Bounded Generic Type

What you are doing is wrong. That's why you get the error. You can call your method with ExceptionC exceptionC=managedException(ExceptionD d) and you will end up with a cast (ExceptionC) exceptionD; And casting it masks the error but you get it at runtime.

Change your method to:

public ParentException managedException(Exception cause) {        
if(ExceptionA.class.isInstance(cause)) {
return ExceptionA.class.cast(cause);
} else if(ExceptionB.class.isInstance(cause)) {
return ExceptionB.class.cast(cause);
} else if(ExceptionC.class.isInstance(cause)){
return ExceptionC.class.cast(cause);
} else {
return new ExceptionD(cause.getMessage(), cause);
}
}

You don't need generics here. All these exceptions are also ParentExceptions so you can juse return them. When you think about it you are trying to make the method return different types. Which cannot be done like that because if you have a variable that is initialized from this method you need to know what would be the result. And you know that the result will be ParentException but you can't know which kind of parent exception is that.

The reason behind it is that your method if written like is not returning ParentException - it is returning T (a subclass). And you can return a different type of subclass and not the one you are trying to get.

In a simpler example if we have:

class A {}

class B extends A{ };

class C extends A{ };

public <T extends A> T test() {
return (T) new B();
}

we can call it with C c=test(); we actually try to cast (C) new B(); which is incompatible but we have masked it and we get the exception at runtime

Why I must declare type parameter before return type in Java?

There are two main points.

First off, as @akuzminykh said in the comments to the question, the ? wildcard is basically just a way to tell the compiler "I don't know what this is gonna be, just assume it could be anything that derives from this class/interface, kthxbye". It doesn't declare any parameter that you could make use of within the method, no identifier you can call upon, nothing. However, type parameters do exactly that, and if you declare a new one, it's a different story than just "calling" the wildcard which you don't have to declare.

Secondly, think of how you would declare a type parameter for a generic class. Do you think this would be enough?

public class Foo {
public T extends CharSequence getBar() {...}
}
public class Foo {
public <T extends CharSequence> getBar() {...}
}

No, none of these two options would work. In order to use a generic type parameter within a class, you have to declare it along with the type itself, not along with the methods/fields that use them. Like this:

public class Foo<T extends CharSequence> {
public T getBar() {...}
}

And in order to use a generic type parameter within a method, you have to declare it along with the method itself or the type that contains the method, not along with the method parameters that use them.

Return Type of Java Generic Methods

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

Sample Image
Sample Image

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

Java Generics return type issue

To answer your edited question,

there's no way to do that without an explicit cast. So the simplest (yet brutal) solution would be:

public <T extends Result> T execute(Command<T> command) {
return (T) new LoginResult();
}

But this way you take the full responsibility for instantiating the right result for the right command, as the compiler won't help you any more.

The only thing that could help you instantiate things dynamically would be a reference to the actual Class<T>.

So if you add a method like Class<T> getResultType() to your command, you would be able to write:

return command.getResultType().newInstance(); // instead of new SpecificResult()

This of course implies that you have a default constructor in each Result implementation and so on...

A more OO friendly approach (no reflection) would be to let the command instantiate its own result (with a factory method T instantiateResult()):

return command.instantiateResult();

Why type parameter required before return type for static generic methods

The type parameter (T) is declared when you instantiate the class. Thus, instance methods don't need a type argument, since it's defined by the instance.

static methods, on the other hand, don't belong to an instance - they belong to the class. Since there's no instance to get the type information from, it must be specified for the method itself.



Related Topics



Leave a reply



Submit