【问题标题】:How to call overloaded method when the parameter is a subclass of a given type?当参数是给定类型的子类时如何调用重载方法?
【发布时间】:2020-03-11 12:54:04
【问题描述】:

我有一个抽象类A、两个名为BC 的子类和两个位于单独类中的重载方法:

public void process(B objB) {...}
public void process(C objC) {...}

我从存储库中检索了一个类型为 A 的实体,我想进行如下调用:

A objA = repo.findById(id);
process(objA);

我知道需要强制转换,但我不想使用 instanceof 或大量 if 语句进行大量检查。

实现这种方法调用的最佳选择是什么? 而且这样的代码设计好不好?

【问题讨论】:

  • 类中有重载的方法吗?
  • 不。他们在一个单独的班级。
  • 你能包含一些你的代码吗?
  • 这样的方法重载不是一个好的架构决策。 Effective Java明智地使用重载一章中广泛介绍了这种做法。
  • 有趣的是,这里的问题源于使用过程式编程BC 是数据类(结构),而处理它们的逻辑位于单独的类(服务或实用程序)中。应用面向对象编程是显而易见的解决方案:将数据及其逻辑组合在一个类中。

标签: java inheritance methods polymorphism overloading


【解决方案1】:

使用instanceof 不是一个好选择,尤其是当您有很多地方需要访问特定子类时。

  1. 显而易见的方法是在基类中添加不带参数的抽象process()方法,并通过从另一个类调用相应的process(this)方法并将thisreference传递给它来在子类中实现它。

  2. 如果希望从类层次结构中划分处理逻辑(可能是您的情况),则有一对模式:Strategy 或更一般的Visitor。 以下是 Visitor 模式用法的示例:

    abstract class A {
        <T> T accept(Visitor<T> visitor);
    }
    
    class B extends A {
        @Override
        public <T> T accept(Visitor<T> visitor) {
            return visitor.visit(this);
        }
    }
    
    class C extends A {
        @Override
        public <T> T accept(Visitor<T> visitor) {
            return visitor.visit(this);
        }
    }
    
    interface Visitor<T> {
        T visit(B bObject);
        T visit(C cObject);
    }
    

    然后通过实现相应的访问者来定义特定的操作:

    Visitor<Integer> visitor = new Visitor<Integer>() {
        @Override
        public Integer visit(B bObject) {
            // TODO do something with bObject
            return null;
        }
    
        @Override
        public Integer visit(C cObject) {
            // TODO do something with cObject
            return null;
        }
    };
    

    然后调用accept:

    A objA = repo.findById(id);
    Integer result = objA.accept(visitor);
    

【讨论】:

  • Visitor 是给定小示例的解决方案。如果将来在A 中添加子类(DEFG),那么访客将成为维护问题,因此我们通过该模式失去了可扩展性。将process() 方法移动到A 并在子类中覆盖它要简单得多,并且不需要传递this
  • 我不坚持使用visitor,我只是描述了不同的可能性。当类层次结构稳定并且需要在这些类上添加各种操作时,访问者很有用。
【解决方案2】:

如果不先转换对象,则无法在此实例中使用重载方法,因为如果显式对象类型不匹配,程序将不会调用重载方法。所以它不会调用 process(B objB) 除非您传递的对象明确属于 B 类。

为了避免太多麻烦,有几种方法可以做到。根据 B 和 C 之间的区别,您可以在从数据库中提取对象时将 A 实例化为该类型(这可以在 RowMapper 中完成)并编写 if else 语句来确定要调用哪个进程:

        if(a.getClass() == B.class)
        {
            return process((B) a);
        }
        else
        {
            return process((C) a);
        }

或者,如果你有更多的子类,你可以使用 switch 语句。这是我能想到的最简单的方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2019-12-24
    • 1970-01-01
    • 1970-01-01
    • 2011-06-03
    • 2011-07-15
    • 2020-10-15
    • 1970-01-01
    • 2015-04-28
    相关资源
    最近更新 更多