How would I overload method in an interface?

2019-03-27 14:34发布

问题:

if I have this interface

public interface someInterface {
  // method 1
  public String getValue(String arg1);
  // method 2
  public String getValue(String arg1, String arg2);
}

I want to be able to pass in 1 or 2 string to the getValue method without having to override both in each implementing class.

public class SomeClass1 impelments someInterface 
{
 @Override
 public String getValue(String arg1);
}

public class SomeClass2 implements someInterface 
{
 @Override
 public String getValue(String arg1, String arg2);
}

this won't work because SomeClass1 needs to implement method 2 and SomeClass2 needs to implement method 1.

Am I stuck doing this?

public interface someInterface2 {
  public String getValue(String... args);
}

public class SomeClass3 implements someInterface2 
{
  @Override
  public String getValue(String... args) {
    if (args.length != 1) {
      throw IllegalArgumentException();
    }
    // code
  }
}

public class SomeClass4 implements someInterface2
{
  @Override
  public String getValue(String... args) {
    if (args.length != 2) {
      throw IllegalArgumentException();
     }
    // code
  }
}

someInterface2 someClass3 = new SomeClass3();
someInterface2 someClass4 = new SomeClass4();
String test1 = someClass3.getValue("String 1");
String test2 = someClass4.getValue("String 1, "String 2");

Is there a better way of doing this?

回答1:

An interface serves as a contract for the users of that interface: you specify what methods are available (in all implementations) and how they are called. If two implementations of an interface need a different method, then that method should not be part of the interface:

public interface Lookup {
}

public class MapLookup implements Lookup {
    public String getValue(String key) {
        //...
    }
}

public class GuavaLookup implements Lookup {
    public String getValue(String row, String column) {
        // ...
    }
}

In your program, you will know which implementation you use, so you can simply call the right function:

public class Program {
    private Lookup lookup = new MapLookup();

    public void printLookup(String key) {
        // I hardcoded lookup to be of type MapLookup, so I can cast:
        System.out.println(((MapLookup)lookup).getValue(key));
    }
}

Alternative approach

If your class Program is more generic and uses dependency injections, you may not know which implementation you have. Then, I would make a new interface Key, which can be either type of key:

public interface Lookup {
    // ...

    public String getValue(Key key);
}

public interface Key {
}

public MapKey implements Key {
    private String key;
    // ...
}

public GuavaKey implements Key {
    private String row, column;
    // ...
}

The dependency injection in your program might come from some factory implementation. Since you cannot know which type of lookup you use, you need a single contract for getValue.

public interface Factory {
    public Lookup getLookup();
    public Key getKey();
}

public class Program {
    private Lookup lookup;

    public Program(Factory factory) {
        lookup = factory.getLookup();
    }

    public void printLookup(Factory factory) {      
        System.out.println((lookup.getValue(factory.getKey()));
    }
}


回答2:

As of Java 8, you can have an interface provide an implementation of a method, through the use of the default keyword. Therefore a new solution would be to provide a default implementation of both methods which maybe throws an exception? Doesn't matter, you decide - then derive the actual implementation from the default interface.

Anyways here is how you can do this:

public interface someInterface {
    // method 1
    default String getValue(String arg1) {
        // you decide what happens with this default implementation
    }

    // method 2
    default String getValue(String arg1, String arg2) {
        // you decide what happens with this default implementation
    }
}

Finally, make the classes override the correct methods

public class someClass1 implements someInterface {
    @Override
    public String getValue(String arg1) {
        return arg1;
    }
}

public class someClass2 implements someInterface {
    @Override
    public String getValue(String arg1, String arg2) {
        return arg1 + " " + arg2;
    }
}

Not bad eh?



回答3:

A solution (not very elegant) might look loke this:

public abstract class SomeClass {
   public String getValue(String arg1) {
      throw new IllegalArgumentException();
   }
   public String getValue(String arg1, String arg2) {
      throw new IllegalArgumentException();
   }
}

public class SomeClass1 extends SomeClass {
   public String getValue(String arg1) {
      // return sth
   }
}

public class SomeClass2 extends SomeClass {
   public String getValue(String arg1, String arg2) {
      // return sth
   }
}

However there's a drawback - SomeClass1 and SomeClass2 can't inherit directly other class.



回答4:

If the second value can be considered optional in a sense and you always have the 2 arguments when calling you could create a wrapper class which implements the 2 parameter interface passing the 1 parameter implementation as a constructor parameter and calling that in the method, e.g. something like this:

interface A{
  method1(P1)
}

interface B{
  method2(P1, P2)
}

class Wrap implements B{
  Wrap(A impl)

  override method2(P1, P2){
    call impl.method1(P1)
  }

}