Follow

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use
Contact

Set the return type parameter based on the Class passed as an argument

Is there any way to set the return value of a method to Encoder<Cat> when Cat.class was passed as an argument?

I’m exploring reworking an API to be as generic as possible. I have a series of interfaces for data encoders but this api is for multiple applications. I want the application author to be able to define the "available encoders" so that their API reflects what’s available accurately.

For example, if app developer Bob wants to offer an encoder for the type Cat he might do:

MEDevel.com: Open-source for Healthcare and Education

Collecting and validating open-source software for healthcare, education, enterprise, development, medical imaging, medical records, and digital pathology.

Visit Medevel

encoderRegistry.register(Cat.class, new CatEncoder());

Then, someone using his apps’ API could get the encoder for a specific type and would see the appropriate type-based arguments:

encoderRegistry.get(Cat.class).encode(Cat cat);

I’m trying to avoid Object types because I want the API to be as user-friendly as possible and if the IDE pops up a method accepting Cat, there’s no question.

So far the only way I’ve been able to get close is by adding a type to the get method:

encoderRegistry.<IEncoder<Cat>>get(Cat.class)

This properly informs the return value for get so that I can use encode(Cat cat) but it’s verbose, and there’s no stopping me from using encoderRegistry.<IEncoder<Cat>>get(Dog.class)

>Solution :

You can set up the API to accept the right types, but internally you will need to type cast. Java’s type system is not strong enough to represent heterogenous type maps, so you will need to depend on the compiler to enforce safety at the API edges. Example:

  interface Encoder<T> {
    String encode(T value);
  }

  static class EncoderRegistry {
    Map<Class, Encoder> encoders = new HashMap<>();
    <T> void register(Class<T> clz, Encoder<? super T> encoder) {
      encoders.put(clz, encoder);
    };

    <T> Encoder<T> get(Class<T> clz) {
      return (Encoder<T>) (Encoder) encoders.get(clz);
    }
  }
  
  public static void run() {
    class Cat {}
    
    class CatEncoder implements Encoder<Cat> {
      @Override
      public String encode(Cat value) {
        return value.toString();
      }
    }

    var registry = new EncoderRegistry();
    registry.register(Cat.class, new CatEncoder());
    registry.get(Cat.class).encode(new Cat());
  }
Add a comment

Leave a Reply

Keep Up to Date with the Most Important News

By pressing the Subscribe button, you confirm that you have read and are agreeing to our Privacy Policy and Terms of Use

Discover more from Dev solutions

Subscribe now to keep reading and get access to the full archive.

Continue reading