tags:

views:

83

answers:

3

If this has already been asked, please link and close this one.


I'm currently prototyping a design for a simplified API of a certain another API that's a lot more complex (and potentially dangerous) to use.

Considering the related somewhat complex object creation I decided to use static factory methods to simplify the API and I currently have the following which works as expected:

public class Glue<T> {

    private List<Type<T>> types;

    private Glue() { types = new ArrayList<Type<T>>(); }
    private static class Type<T> {
        private T value;
        /* some other properties, omitted for simplicity */
        public Type(T value) { this.value = value; }
    }

    public static <T> Glue<T> glueFactory(String name, T first, T second) {
        Glue<T> g = new Glue<T>();
        Type<T> firstType = new Glue.Type<T>(first);
        Type<T> secondType = new Glue.Type<T>(second);

        g.types.add(firstType);
        g.types.add(secondType);
        /* omitted complex stuff */
        return g;
    }
}

As said, this works as intended. When the API user (=another developer) types Glue<Horse> strongGlue = Glue.glueFactory("2HP", new Horse(), new Horse()); he gets exactly what he wanted.

What I'm missing is that how do I enforce that Horse - or whatever is put into the factory method - always implements both Serializable and Comparable? Simply adding them to factory method's signature using <T extends Comparable<T> & Serializable> doesn't necessarily enforce this rule in all cases, only when this simplified API is used. That's why I'd like to add them to the class' definition and then modify the factory method accordingly.

PS: No horses (and definitely no ponies!) were harmed in writing of this question.

+1  A: 

I guess the only proper solution would be the remove the possibility of using the 2nd API you mention, and add the type constraints you describe to the static factory method(s) above.

If you can't deprecate the 2nd API, you can't enforce such constraints. You can of course add the proper interface implementations to your classes, but the 2nd API won't require it, so there will always be a hole where errors can slip through.

Péter Török
Yeah, deprecation is not an option at this point unfortunately. The point of this simplified API is to hide the horror from new guys and let the old stubborn people have their complex API with all of its oddities.
Esko
A: 

In addition to the generic type bounds (which are non-reifed), you can do run-time checks if that's what you want, i.e.

if (first instanceof Comparable && first instanceof Serializable) {
   // everything's fine...
} else {
   throw new IllegalArgumentException("what are you doing?");
}
polygenelubricants
+4  A: 

You're actually very close. Here's my solution

public class Glue<T extends Serializable & Comparable<T>> {

  private List<Type<T>> types;

  private Glue() { types = new ArrayList<Type<T>>(); }
  private static class Type<T> {
      private T value;
      /* some other properties, omitted for simplicity */
      public Type(T value) { this.value = value; }
  }

  public static <V extends Serializable & Comparable<V>> Glue<V> glueFactory(
      String name, V first, V second) {
      Glue<V> g = new Glue<V>();
      Type<V> firstType = new Glue.Type<V>(first);
      Type<V> secondType = new Glue.Type<V>(second);

      g.types.add(firstType);
      g.types.add(secondType);
      /* omitted complex stuff */
      return g;
  }
}

public class Horse implements Serializable, Comparable<Horse> {
  private static final long serialVersionUID = 1156763996034008367L;

  @Override
  public int compareTo(Horse o) {
     return 0;
  }      
}

public class Cat  { }

public static void main(String[] args) {
  Glue<Horse> gh = Glue.glueFactory("2HP", new Horse(), new Horse());
  Glue<Cat> gc = Glue.glueFactory("2C", new Cat(), new Cat()); // <--- Does not compile, as requested!!
}

This program is different from your code in these aspects:

  • I renamed the type parameter T (of the glueFactory() method) to V. Given that the method is static its type parameter has no relation to the containing class type parameter and thus the code is much clearer when the two have different names.
  • I added the bounds (implements Serializable & Comparable ...) to the declaration of both V and T

As expected the resulting program compiles Glue.glueFactory("2HP", new Horse(), new Horse()); but rejects Glue.glueFactory("2C", new Cat(), new Cat());

Itay
Ahhh, I should've decoupled `T` and `V` as you did. Seems to be working correctly now. Thanks! :)
Esko