【问题标题】:Java Interface method supporting variable number of arguments with different typesJava接口方法支持可变数量的不同类型的参数
【发布时间】:2021-07-19 10:53:35
【问题描述】:

我想构建一个接口,其中包含一个方法,该方法可以接受可变数量的各种类型的参数,如下所示:

interface MyInterface {

   boolean myMethod(Object ...args); <--HERE I WOULD LIKE TO PASS ANY NUMBER OF PARMS OF ANY TYPE

}

在接口的具体实现中,我想传递任意数量的任意类型的参数如下:

class MyImpl1 implements MyInterface {

   boolean myMethod(Student s, Course d) {
      ...
   }
}

另一种实现方式如下:

 class MyImpl2 implements MyInterface {

   boolean myMethod(Department d) {
      ...
   }
}

在这方面,我在这里浏览了一些帖子,但对如何执行此操作一无所知。有人可以帮忙吗?谢谢。

编辑:

我想到了确切的接口结构如下:

interface ConditionEvaluator {
     
     boolean evaluate(Object ...args);
     CondtionName getConditionName();
   }

一个具体的实现如下所示:

class StudentEvaluator implements MyInterface {
  
    boolean evaluate(Student s, List<Course> courses) {
      ...
    }
  
    ConditionType getConditionType() {
       return ConditionType.STUDENT_PASSED;
    }
 }

要使用它,我想,我将创建一个枚举映射,其中包含 ConditionType 作为键和具体实现作为值,如下所示:

Map<ConditionType, MyInterface> conditionTypeMap;

然后从调用者方法开始,正在考虑基于适当的ConditionType 派生适当的实现。

用法有点像下面,

我将有一个工厂类如下:

@Component
public class ConditionEvaluatorFactory {

  private final Map<ConditionType, ConditionEvaluator> conditionEvaluatorMap;

  public ConditionEvaluatorFactory(List<ConditionEvaluator>
                                          conditionEvaluatorList) {
    Map<ConditionTyope, ConditionEvaluator> evaluatorMap =
        new EnumMap<>(ConditionEvaluator.class);
    for (ConditionEvaluator evaluator :
        conditionEvaluatorList) {
      evaluatorMap.put(evaluator.getConditionType(),
          evaluator);
    }
    this.conditionEvaluatorMap = evaluatorMap;
  }

  public ConditionEvaluator getEvaluator(ConditionType conditionType) {
    ConditionEvaluator evaluator = conditionEvaluatorMap
        .get(conditionType);
    if (evaluator == null) {
      throw new Exception("error message");
    }
    return evaluator;
  }
}

调用者类如下:

@component
class StudentUtil {

 @Autowired
 private ConditionEvaluatorFactory evaluatorFactory;

 public void callerFunc(Student s, List<Course> courseList) {
    boolean flag = evaluatorFactory.getEvaluator(ConditionType.STUDENT_PASSED).evaluate(s, courseList);
 ...
    
 }

【问题讨论】:

  • 你能解释一下拥有接口的意义吗?也就是说,您是否将拥有引用 MyInterface 类型对象并在其上调用 myMethod 的代码?
  • 恐怕这在 Java 中是不可能的。但是,您对这种模式的实际用法是什么?那个界面在这里似乎没有太大的用途。
  • 如果参数没有在接口中定义并且可以是任意的,那么任何调用代码如何能够使用MyInterface 的实例。
  • @sp00m,实际上,我希望有一个通用接口,该接口将有一个单一方法采用不同的参数并将它们评估为真/假。在我的应用程序中,有一些用例可以评估这些条件。这就是为什么我想有一个通用的接口并相应地提供具体的实现。现在,正如您所说,这是不可能的,我不明白,这样做的最佳方法是什么。
  • “实际上,我希望有一个通用接口,它有一个采用不同参数的单一方法” - 你可以有一个通用接口或采用完全不同参数的方法。两者一起根本没有意义 - 如果方法具有完全不同的参数,它们不能共享一个公共接口,它们根本不符合单个接口,即使在字面解释中,不管 java。

标签: java interface


【解决方案1】:

让我们从重要的一点开始:我们之所以有一个由多个子类实现的interface,是因为所有这些类共享相同的逻辑,即使它们遵循不同的逻辑实施。

在您的情况下,您似乎无法真正拥有一个接口(或者至少,您必须接受未知类型的变量列表并在某些时候将它们转换为静态类型,才能做到这一点)。

对于你的问题确实没有理想的解决方案,但我还是试着编造了一些东西。

如果您的所有目标是有一个点,您只需根据要检查的条件调用评估器,您可以做的事情是声明一个包含输入映射的类:

public class Inputs {

    private final Map<String, Object> inputs = new HashMap<>();

    public static Inputs builder() {
        return new Inputs();
    }

    public Inputs push(String key, Object value) {
        inputs.put(key, value);
        return this;
    }

    public <T> T get(String key) {
        return (T) inputs.get(key); //<-- Unchecked cast, but somewhere you have to do it unfortunately
    }

}

然后,在您调用ConditionType 的枚举中,添加一个abstract 方法,该方法接受Inputs 参数并返回boolean 作为评估结果。

一旦你这样做了,你就可以在枚举的每个特定值中实现特殊处理:

public enum ConditionType {
    STUDENT_PASSED {
        @Override
        public boolean evaluate(Inputs inputs) {
            Student student = inputs.get("student");
            List<Course> courses = inputs.get("courses");
            //do your logic with student and courses
            return false;
        }
    },
    DEPARTMENT_OPEN {
        @Override
        public boolean evaluate(Inputs inputs) {
            Department department = inputs.get("department");
            //do your logic with department
            return false;
        }
    };

    public abstract boolean evaluate(Inputs inputs);
}

这样,在您的实用程序类中,您将能够执行以下操作:

public class EvaluationUtils {

    public void callerFunc(Student student, List<Course> courses) {
        Inputs input = Inputs.builder()
                .push("student", student)
                .push("courses", courses);
        boolean flag = ConditionType.STUDENT_PASSED.evaluate(input);
        //...
    }

    public void callerFunc(Department department) {
        Inputs input = Inputs.builder()
                .push("department", department);
        boolean flag = ConditionType.DEPARTMENT_OPEN.evaluate(input);
        //...
    }

}

我在上述方法中看到的唯一优点是它避免了您在每个实现中进行检查和强制转换。

然而,它与您最初认为的声明接口(@98​​7654329@)并没有真正的不同,每个实现都会获取参数,检查大小,类型然后继续。但是话又说回来,您的案例听起来不像是可以分解为接口或任何共享方法的案例。

【讨论】:

  • 这个答案有帮助。非常感谢。一件小事,在您在答案中提供的代码 sn-p 中,我认为,您必须在通过键从地图中获取值时进行类型转换。
  • @Joy 不,我使用的不是地图的 get 方法,而是我班级的 get 方法。在那个 get 方法中,我已经将类型推断到签名中,所以我不需要强制转换。你可以自己试试看:)
  • 附言。正如我在评论中所说,不幸的是,这个解决方案需要经过未经检查的演员才能工作。执行未经检查的强制转换的唯一地方是 get 方法本身,但其余代码将使用静态类型的对象。
【解决方案2】:

这就是泛型的用途。这样定义你的界面:

interface MyInterface<T> {

    boolean myMethod(T arg);
}

那么您的基本单参数实现将是:

class MyImpl2 implements MyInterface<Department> {

    boolean myMethod(Department d) {
        ...
    }
}

你的两个参数实现需要一个持有者类:

class CourseEnrollment {
    Student s; Course d;
}

class MyImpl1 implements MyInterface<CourseEnrollment> {

    boolean myMethod(CourseEnrollment ce) {
        ...
    }
}

【讨论】:

  • 我不认为他想要那样,因为每个实现的参数数量可能会改变。
  • 我认为这不会解决问题,因为未知参数类型的实例必须声明为MyInterface&lt;?&gt;,这使得无法在此实例上调用该方法。
  • @Izruo 如果您只使用原始类型,则可以调用它...但我认为我们不了解 OP 的期望。
  • @tgdavies 如果我做对了,我认为他想要一个接口来一直调用“.myMethod(”,其中的一些参数会有所不同,并且根据他想要的这些参数的数量和数量而定落入正确的实现。我认为他想要一个访问者模式。
  • @tgdavies 如果你必须使用原始类型来调用方法,你可以摆脱泛型,而是使用Object作为参数类型,从而呈现泛型没用。您最终会得到更少的编译器警告和相同的功能。
猜你喜欢
  • 2017-07-08
  • 1970-01-01
  • 2012-04-28
  • 2013-11-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-12
相关资源
最近更新 更多