I’m using a class that uses two generic type parameters in Java, but I find the second type parameter to be redundant because it is already determined by the first.
Here’s the current class declaration:
public abstract class GameScreen<C extends Controller<G>, G extends Game> extends MPScreen {
public G getGameInstance() {
// ...
}
public C getController() {
// ...
}
// ...
}
Controller
is a generic type that takes a Game
type, like so:
public abstract class Controller<G extends Game> {
// ...
}
I would like to avoid having to specify the second type parameter (G
) directly because it’s already determined by C
. However, I still want to use G
as the return type in some methods (like getGameInstance()
).
I tried to rewrite the class like this:
public abstract class GameScreen<C extends Controller<? extends Game>> extends MPScreen {
public C getController() {
// ...
}
}
But with this approach, I lose access to G
as a distinct type, so I can’t use it as a return type in methods like getGameInstance()
.
My question is:
Is there a way in Java to infer the type parameter G
from the type C
without explicitly declaring G
as a second type parameter? Or is there a cleaner way to achieve this while still having G
as a method return type?
1
In Java, there is no direct way to infer the type parameter G from the type C without explicitly declaring it as a second type parameter. However, there is an alternative approach you can consider to achieve your goal.
One option is to introduce a helper method that extracts the type parameter G from the type parameter C. Here’s an example:
public abstract class GameScreen<C extends Controller<?>> extends MPScreen {
public abstract C getController();
public Class<?> getGameType() {
Type type = getClass().getGenericSuperclass();
ParameterizedType paramType = (ParameterizedType) type;
Type[] typeArguments = paramType.getActualTypeArguments();
return (Class<?>) typeArguments[0];
}
public Game getGameInstance() {
Class<?> gameType = getGameType();
// ...
}
}
In this example, the getGameType() method uses reflection to extract the type parameter G from the type parameter C. It retrieves the actual type arguments from the generic superclass of the GameScreen class and returns the first type argument. This way, you can still access G as a distinct type and use it as the return type in methods like getGameInstance().
Keep in mind that using reflection can have performance implications, so use it judiciously.
ADD
public abstract class GameScreen<C extends Controller<G>, G extends Game> extends MPScreen {
private C controller;
public abstract C createController();
public G getGameInstance() {
if (controller == null) {
controller = createController();
}
return controller.getGame();
}
public C getController() {
if (controller == null) {
controller = createController();
}
return controller;
}
}
I introduce a new abstract method createController(), which is responsible for creating an instance of the controller. This method will be implemented in concrete subclasses of GameScreen to create the specific controller instance.
The getGameInstance() method now returns the Game object obtained from the controller, ensuring that the correct type is returned based on the type parameter G.
By storing the controller instance in a field and lazily initializing it, avoid creating multiple instances of the controller while still ensuring that the correct type is used.
feifei is a new contributor to this site. Take care in asking for clarification, commenting, and answering.
Check out our Code of Conduct.
1