1

So I have a series of related interfaces one that contains other 3 (enforced by setters and getters).

enter image description here

While instances of this interfaces are OK to remain as just interface contracts while they are managed (stored and retrieved from a map); when they reach a processing bean they need to be able to reveal them-self as the implemented class they are.

So I was able to this doing the following:

public interface IState<S extends IState> {
<G extends GameData> G getGameData();
<G> S setGameData(G gameDataIO);

<P> P getPlayer();
<P> S setPlayer(P player);

<GS> GS getGameState();
<GS> S setGameState(GS gameState);}

The default implementation:

public class DefaultStateNG implements IStateNG<DefaultStateNG> {

private GameData gameDataIO;
private IPlayerState player;
private IGameState gameState;


@Override
public <G extends GameData> G getGameData() {
    return (G) gameDataIO;
}

@Override
public <G> DefaultStateNG setGameData(G gameDataIO) {
    this.gameDataIO = (GameData) gameDataIO;
    return this;
} //..... other accessor methods implemented

So when I use them in the processor bean they do not need to be casted into the appropriate implementation of the interface.

The problem arises when a unchecked cast in throw when casting in the returned type. I don't know how to handle such warning. I mean I am setting gameDataIO as Game data type and the return is supposed to be a GameData type. Why the warning?. I tried every kind of casting, the only one that works without warning so far is is pass the expected class as argument and then use type.cast(returnedObject).

Any hint will be appreciated.

jfzr
  • 374
  • 4
  • 17
  • 2
    I dont think you want to use generics here. Generics are used if you want to use classes from different trees without using their combined parent (Object in worst case scenario). Here, you just want to have GameData, Player, etc. – Wietlol Mar 22 '17 at 23:42
  • GameData, Player and other are interfaces whose implementations are injected by Spring on start. So I have interfaces just to ensure basic functionalities, but the implemented classes need to be known by the processor. I did a first version without generics, but I ended doing tons of type casting making the code unreadable. – jfzr Mar 23 '17 at 20:11

2 Answers2

3

When your interface defines generic methods like this:

<G extends GameData> G getGameData();

The compiler knows that <G> must extend or implement GameData, but the exact implementation represented by G will be inferred at the site of the code that calls the getGameData() method.

When you implement that method:

@Override
public <G extends GameData> G getGameData() {
    return (G) gameDataIO;
}

Your implementation here knows nothing about any potential call sites. It's supposed to return the specific subtype needed by the caller (G), which it cannot do. You're casting, but the compiler can't verify that it's safe (because it's not) and so you get an unchecked warning.

Pretend I write a class MyGameData extends GameData and write this in my code:

IState<?> state = ... // TODO
MyGameData mgd = state.getGameData();

In this case, <G> is bound to the specific type MyGameData, which meets your API requirement that it must inherit from GameData. But then your code returns a plain GameData not my specific subclass and results in a ClassCastException at runtime.

The same goes for most of your other methods. Either return the base type (GameData, etc.) without making the getters, etc., generic methods or you'll have to add type parameters G, GS, and P to the IState interface instead of on a per-method basis.


EDIT to add, based on this comment:

GameData, Player and other are interfaces whose implementations are injected by Spring on start. So I have interfaces just to ensure basic functionalities, but the implemented classes need to be known by the processor. I did a first version without generics, but I ended doing tons of type casting making the code unreadable.

You're describing symptoms of a tightly-coupled system. Interfaces are a tool for defining loosely-coupled systems, as these are often considered a better design. I encourage you to consider why in your design the processor needs to be so aware of the implementation type of the other interfaces. Is it because the interfaces themselves do not fully describe the functional behavior needed by implementations of that interface?

Community
  • 1
  • 1
William Price
  • 4,033
  • 1
  • 35
  • 54
  • I see your point, but in my implementation of the processor bean a set the input to be an implementation of IState, so the incoming parameter is `DefaultState state`; then I can do `MyGameData mgd = state.getGameData()`; without problems. I got my code working, and it is just that warning that bothers me. From what you said I get why I am getting the warning, and I cannot ensure the type safety, so I just should go with a suppress warning tag? – jfzr Mar 23 '17 at 20:08
  • I don't recommend suppressing this warning (or warnings in general), because it means things _will break_ unless the circumstances _happen to be_ just right. That's a lot of wishy-washy hand-waiving that can cause you trouble later. *My recommendation* is that you change your API to not use generics for these methods, because they cause your API to suggest it supports something that your implementation cannot. – William Price Mar 23 '17 at 20:14
  • Indeed my objective was to have a very _loosely-coupled_ system. I know what I am doing sounds counter intuitive but the reason why the processor needs to know the implementation is that the processor itself is another injected interface. So what I am doing is a generic game server, where the states are stored in different stores. Every part of the software should be able to be interchangeable (config). So the the State/Player Manager don't care about the details of the implementation, but game itself does and it's the game that uses specific fields that may not be visible in the interface. – jfzr Mar 23 '17 at 20:39
  • 1
    A game and its own state would be justifiably tightly-coupled, but then I wouldn't expect them to be injected/managed as _separate beans_. Retrieving saved game-specific state implementation from a general state manager is going to _require_ a cast somewhere; due to erasure this isn't something that generics can solve on its own. – William Price Mar 24 '17 at 22:30
0

Well I came around with solution which is to define the type on the entry point to the implemented processor. I was no able to find a way to to set the type declaration using generic methods (at least no without to having to do a un unchecked casting).

After William I am clear the declarations must be made, and my processor bean is where all other bean get connected together, hence it made sense that there the types are declared. Unluckily for me the state is the I/O object of my processing method, so it also need to declared in the processor including it's contained classes.

Something like:

@Component
public class KenoManager implements IGenericGame<DefaultState<
    KenoData,
    DefaultPlayer,
    KenoGameState,
    DefaultSystemState>> {

public DefaultState processInit(DefaultState<
        KenoData,
        DefaultPlayer,
        KenoGameState,
        DefaultSystemState> state) {
    return null;
}

When it come the config file I can use wildcards ? to fill the declaration and to make the injection of certain beans generic.

Community
  • 1
  • 1
jfzr
  • 374
  • 4
  • 17