【问题标题】:How would I overload method in an interface?如何在接口中重载方法?
【发布时间】:2023-03-19 20:00:02
【问题描述】:

如果我有这个界面

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

我希望能够将 1 或 2 个字符串传递给 getValue 方法,而不必在每个实现类中都重写。

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

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

这行不通,因为 SomeClass1 需要实现方法 2,而 SomeClass2 需要实现方法 1。

我坚持这样做了吗?

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");

有更好的方法吗?

【问题讨论】:

  • 你应该使用2个接口。
  • 让你的课程abstract。否则,您的类必须实现接口的两种方法。由于示例的简单性,将其分成两个界面将没有任何帮助。
  • 如果你传入一个字符串并且类只实现了双参数方法,你会发生什么,反之亦然?
  • 你到底想做什么?您描述的情况有代码设计问题的味道。
  • 您的两个类的行为应该不同,因此它们不应实现相同的接口(即使它们的方法名称可能相同,但它们的约定却不同)。正如 Sotirios 建议的那样,使用两个接口。

标签: java oop interface


【解决方案1】:

接口充当该接口用户的契约:您指定可用的方法(在所有实现中)以及如何调用它们。如果一个接口的两个实现需要不同的方法,那么该方法应该是接口的一部分:

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) {
        // ...
    }
}

在你的程序中,你会知道你使用的是哪个实现,所以你可以简单地调用正确的函数:

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));
    }
}

替代方法

如果你的类Program 更通用并且使用依赖注入,你可能不知道你有哪个实现。然后,我会创建一个新的接口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;
    // ...
}

您程序中的依赖注入可能来自某些工厂实现。由于您不知道您使用哪种类型的查找,因此您需要为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()));
    }
}

【讨论】:

  • 感谢这两种解决方案都有帮助
  • 那你不认为访客模式违反了这一点吗?
  • @zgulser 你能详细说明一下吗?
  • @VincentvanderWeele 出于某种原因,我完全误会了你。很抱歉浪费时间。
【解决方案2】:

从 Java 8 开始,您可以通过使用 default 关键字让接口提供方法的实现。因此,一种新的解决方案是提供两种可能引发异常的方法的默认实现,然后从默认接口派生实际实现。

无论如何,你可以这样做:

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
    }
}

最后,让类覆盖正确的方法

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;
    }
}

【讨论】:

    【解决方案3】:

    一个解决方案(不是很优雅)可能看起来像这样:

    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
       }
    }
    

    但是有一个缺点 - SomeClass1 和 SomeClass2 不能直接继承其他类。

    【讨论】:

    • 这实际上与我发布的解决方案非常接近。就像你可以看到未来......
    • 看起来很合理,但抛出异常的空方法违反了 LSP(Liskov 替换原则)。它们几乎就像违反合同一样,让您的 API 用户感到惊讶。尝试将项目添加到您从 Arrays.asList 获得的列表中,看看感觉如何。
    【解决方案4】:

    如果第二个值在某种意义上可以被认为是可选的,并且在调用时您总是有 2 个参数,那么您可以创建一个实现 2 参数接口的包装类,将 1 参数实现作为构造函数参数传递并在方法中调用它,例如像这样:

    interface A{
      method1(P1)
    }
    
    interface B{
      method2(P1, P2)
    }
    
    class Wrap implements B{
      Wrap(A impl)
    
      override method2(P1, P2){
        call impl.method1(P1)
      }
    
    }
    

    【讨论】:

      【解决方案5】:
      public interface SomeInterface {
          default void print(String s) {
              System.out.println(s);
          }
      }
      
      public class SomeClass implements SomeInterface {
          /**
           * Note the this overloads {@link SomeInterface#print(String)}, 
           * not overrides it!
           */
          public void print(int i) {
              System.out.println(i);
          }
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2011-06-15
        • 1970-01-01
        • 2022-01-23
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多