Optional or default generic parameters

12.1k views Asked by At

Say I have a 3 generic parameters:

  public static interface Mapper<V,T,E> {
    public void map(KeyValue<V> v, AsyncCallback<T,E> cb);
  }

How can I make the parameters optional? How can I give the parameters default values if the user only supplies the first parameter?

Using TypeScript, that would look like:

  public static interface Mapper<V,T = any,E = any> {
    public void map(KeyValue<V> v, AsyncCallback<T,E> cb);
  }

so if the user doesn't supply T and E, they default to any. Is there a way to do this with Java?

5

There are 5 answers

0
Tom Hawtin - tackline On

Like many languages, there is no optional or gradual typing in Java. The typing rules are probably complicated enough as it is. Nor are there default type arguments, but that doesn't seem to be the major issue here.

In your case, it looks like making the typing more client friendly solves the problem without having to go further. I am assuming the E in AsyncCallback is for "exception" and T is, as in GWT's AsyncCallback, for a method parameter.

public static interface Mapper<V,T,E> {
    public void map(KeyValue<V> v, AsyncCallback<? super T,? extends E> cb);
}

That allows any particular Mapper to take any applicable AsyncCallback.

We can be more explicit about the exception - useful if we need to define a throws anywhere.

public static interface Mapper<V,T,E extends Throwable> {

If a reference to a Mapper could take any AsyncCallback, declare it of type Mapper<V,Object,Throwable> for some V.

If you desperately wanted a short form of Mapper with a concise declaration for you could introduce an adapter.

public class MapperAny<V> implements Mapper<V,Object,Throwable> {
    private final Mapper<V,Object,Throwable> target;
    // A static creation method would be nicer, but ctor conventional.
    public MapperAny(Mapper<V,Object,Throwable> target) {
        this.target = target;
    }
    public void map(KeyValue<V> v, AsyncCallback<T,E> cb) {
        target.map(v, cp);
    }
    // unwrap
    public Mapper<V,Object,Throwable> mapper() {
        return target;
    }
}

If you wanted the same thing for AsyncCallback, it's a bit more difficult. There is no denotable opposite of Object (i.e. the type of null).

0
Peng Fei Dong On

I'd say no. Java enjoys it when all of it's variables are set as a certain type and never change. If you want to find the most flexibility, try finding the biggest possible superclass, as in maybe T = Object and E = Object. Later when you need to use the object you can typecast it into what you want? Maybe?

0
Shoaib Anwar On

If what you have in mind is something along the lines:

  • Having a method that takes 3 (generic) parameters
  • But at times you'll want it to have 2 parameters

Then, the following is the way to approach this. Since otherwise making parameters optional is not allowed in my opinion.

interface InterfaceA<T, T> {
      boolean myMEthod(T clazz, UserRef user, String type, T clazz2) {
             return false;
      }
}  

interface InterfaceB<T> extends InterfaceA<T, T> {
      default boolean myMEthod(T clazz, UserRef user, String type) {
             return myMEthod(clazz, user, type, clazz);
      }
}   

This allows you to implement the InterfaceB whereever you want to skip a parameter. You could pass same parameter (of same type) twice, pass null, or some default value.
I hope this works for your case.

0
Ctorres On

I see two ways of doing it (or at least workaround the problem):

  1. The first way is to implement a method with all your parameters and pass null instead of the parameter if you don't need it. Then handle it in your method's body. This should look like this:

    public void myMethod(Sometype param1, Sometype param2, Sometype param3) {
        //Test if your paramaters are null or not then do what you want
    }
    

    And you can call your method like this (if you don't need the second parameter for example): myInstance.myMethod(param1, null, param2);

    You could do it like this, but I DON'T recommand using this trick, because it's counterintuitive for the user, and your method will be verbose, and "unclean"...

  2. There's a second way to do it. even if it's way more verbose than TypeScript or Kotlin, you can simply define several methods with the required parameters. For example one method with only the second parameter, one with the first and the third parameter, etc... It's just basic method overloading, but it works perfectly! I think it's one of the most "java-friendly" way to do it.

I hope I helped you

0
Magnus Reftel On

You can´t do it with one class, but you can partially solve it using inheritance. Say you have

 interface Mapper<V,T,E> {
    public void map(KeyValue<V> v, AsyncCallback<T,E> cb);
  }

You can then have interfaces that specialize this, like so:

interface MapperWithObjectValue<T,E> extends Mapper<Object, T, E> {
}
interface MapperWithObjectResult<V,E> extends Mapper<V, Object, E> {
}

etc.

It´s not all that useful in this way, as the user could just as well pass Object as the type parameter, but it does have its uses for retroactively generifying an interface. Say you have

interface Foo {
  Object getFoo();
}

You then realize that you´d like that return type be a generic type, but you´ve already committed this interface as part of your stable API. You can then use specialization via inheritance to solve it, like so:

interface GenericFoo<T> {
  T getFoo();
}
interface Foo extends GenericFoo<Object> {
}

Existing code can keep using Foo just like before, and code that has a need for a different return type can use GenericFoo instead.