Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can I pass a complex type structure in Java generics?

I am currently trying to implement an API for a conceptual model using Java interfaces and generics. The model (Transmodel V5.0) is described as an entity-relationship model in great detail, but it does not specify some of the base types used. For example the types of identifiers for the various entities or the types used to establish ordering in sequences are not defined.

Since I want to keep the API as generic as possible I started using generics to configure those details. I do not want to make any assumptions about the types, including not assuming that anything is consistent. Each entity can have a different identifier type, each sequence can have a different type used for ordering purposes.

The problem I am facing is that the complexity grows fast once one entity references another -- not only do I need to pass the types for its identifier, but also everything needed to configure the referenced entity.

For example I have:

/**
 * @param <ID> The type for the identifier of this entity.
 * @param <ID_JP> The type identifying journey patterns.
 * @param <OP_JP> The ordering used for points in journey patterns.
 * @param <JP> The type of journey pattern referenced by this entity.
 */
public interface VehicleJourney<
        ID,
        ID_JP, OP_JP extends Comparable<OP_JP>, JP extends JourneyPattern<ID_JP, OP_JP>
    > extends IdentifiableObject<ID>
{
    JP getJourneyPattern();
}

I can still read that and make sense of it, but it is getting more than a bit verbose. And then entities like the VehicleJourney can be referenced in other entities, which makes the type parameter list explode. This is pretty much the smallest non-trivial example I can think of.

Is there a way to create a single Java entity modelling the whole configuration of the type system? I am thinking of something that would have all the identifier types and ordering types attached and can then be passed around as one, turning the example above into something like this:

public interface VehicleJourney<CONF, JP extends JourneyPattern<CONF>> 
       extends IdentifiableObject<???>
{
    JP getJourneyPattern();
}

In the spot with the question marks the type of the VehicleJourney identifier would have to be extracted from CONF somehow. If that would be feasible, then the complexity should stay at a manageable level.

like image 913
Peter Becker Avatar asked Jan 31 '14 01:01

Peter Becker


1 Answers

Not going to be pretty.

You can use unanchored types, and simplify by having a type that groups the id and comparator types:

public interface Meta<ID, COMP extends Comparable<COMP>> {

}

public interface IdentifiableObject<M extends Meta<?, ?>> {

}

public interface JourneyPattern<M extends Meta<?, ?>>
        extends IdentifiableObject<M> {

}

public interface VehicleJourney<M extends Meta<?, ?>, JP extends JourneyPattern<?>>
        extends IdentifiableObject<M> {

}

You really don't need to give names to anchor the nested types of the types. When you have the concrete class (or even sub-interfaces), all the types will be anchored anyway. For example:

public class VehicleJourneyImplMeta implements Meta<String, String> {

}

public class VehicleJourneyImpl extends IdentifiableObjectBase<VehicleJourneyImplMeta> 
        implements VehicleJourney<
                VehicleJourneyImplMeta, 
                JourneyPattern<Meta<Integer, String>>> {

}

You will need to delegate accessing of the types in Meta by using intermediate classes, most likely anonymous ones:

VehicleJourney<Meta<String, String>, ?> v = something();
Meta<String, String> m = v.getObjectMeta();
String idOfV = m.getId();

With help of various type parameters at method level, you can probably make this work.

Seems to me what you (and me, and most people) really want is something like this:

public interface VehicleJourney<M extends Meta<?, ?>, JP extends JourneyPattern<?>>
        extends IdentifiableObject<M> {

        public JP.M.ID getIdOfReferencedX();
}

Unfortunately Java does not support typing JP.M.ID as return type. Maybe someone will raise a JSR for this. Bytecode contain names of generic type parameters, if I remember correctly.

like image 144
One.Punch.Leon Avatar answered Oct 26 '22 23:10

One.Punch.Leon