Generics aren't necessarily the evil, it is just better to use them internally and not expose them. For example, I have this declaration in my DynamicTree broadphase.
class DynamicTree<AABBType extends DynamicTree.AABB, CollisionType>
That is because I have the same implementation for both 2D and 3D scenes! I just require the AABB type to follow an interface, and CollisionType is simply the component type, CollisionComponent2D and CollisionComponent3D are the actual types that get there.
However, it is bad if you expose this class to the user, as that will make the user to use generics in all the type declarations, so I have wrapper classes DynamicTree2D and DynamicTree3D that I expose, they just use the DynamicTree class internally.