【问题标题】:Alternative to instanceof approach in this case在这种情况下替代 instanceof 方法
【发布时间】:2023-03-04 01:55:01
【问题描述】:

您好,我想知道有什么比这样更优雅的替代方案:

class Base...

class A extends Base...

class B extends Base...

//iterator of colection containing mixed As and Bs i want to remowe Bs and do omething with As
while(iterator.hasNext()) {
    Base next = iterator.next();
    if(next instanceof A) // do something
    if(next instanceof B)
        iterator.remove();
}

看看有什么替代品...

感谢您的建议。

编辑:基类可能有许多子类,而不仅仅是两个,而且它们的数量可能会随着时间增长

【问题讨论】:

    标签: java instanceof


    【解决方案1】:

    您可以在Base 中创建方法并在AB 中覆盖它们。

    例如:

    class Base{
        public boolean shouldRemove(){
            return false;
        }
        public void doSomething(){
        }
    }
    
    class A extends Base{
        @Override
        public void doSomething() {            
        }
    }
    
    class B extends Base{
        @Override
        public boolean shouldRemove() {
            return true;
        }
    }
    

    然后你不需要知道对象是哪个类的实例:

        while(iterator.hasNext()) {
            Base next = iterator.next();
            if(next.shouldRemove()){
                iterator.remove();
            }
            else{
                next.doSomething();
            }
        }
    

    【讨论】:

      【解决方案2】:

      您真的需要从列表中删除它们吗?你为什么不拥有在Base 类中做某事的方法(什么都不做),然后在A 类上覆盖它做你想做的事情。

      class Base{
          public void doSomething(){
          }
      }
      
      
      class A extends Base{
          @Override
          public void doSomething(){
              // do something
          }
      }
      

      然后您可以遍历列表并在所有对象上调用方法 doSomething。

      for(Base base : list) {
          base.doSomething();
      }
      

      这样,只有重写了doSomething() 方法的类才会真正做一些事情。所有其他类将只执行 Base 类中的虚拟实现。

      如果Base 是一个抽象类,您可以将doSomething() 声明为抽象类并让扩展类实现它。使用这种方法,所有类都必须实现您不希望执行任何计算的方法和类,您只需提供该方法的虚拟实现。或者,您甚至可以使用doSomething() 方法创建一个接口并拥有(这甚至可能是一个更好的决定)并让Base 类实现它,因为只有扩展类才能实际实现该方法。

      【讨论】:

        【解决方案3】:

        instanceof 是按类型过滤对象的好方法 - 这就是您想要做的。您有一个混合集合,因此您需要某种过滤器,或者过滤输入(只存储 As)或过滤输出(只处理 As)。

        如果您只是不喜欢“instanceof”,您可以使用enum 指定类型并添加最终方法以获取Base 的类型:

        enum Type { ATYPE, BTYPE };
        

        public Base {
        
           final private Type type;
           public Base(Type type) { this.type = type; }
           public Type getType() { return type; }
           // ...
        }
        

        public A {
           public A() { super(Type.ATYPE); }
        }
        

        while(iterator.hasNext()) {
            Base next = iterator.next();
            switch (next.getType) {
              case ATYPE: // do something and break
              case BTYPE: iterator.remove(next); break;
            }
        }
        

        【讨论】:

          【解决方案4】:

          我认为这是一个非常简短和清晰的解决方案,并且没有其他选择(没有代码增长), 在第二种情况下,只需添加 else if 而不是 if

          你也可以在函数调用上拆分代码,if语句不会很大

          另一种解决方案是创建Map 的将被调用的委托。像这样: interface ISimpleDelegate{ void doSomeLogic(Base b) } `映射委托 = new HashMap();

          在此之后将您的逻辑添加为实现 ISimpleDelegate 的匿名类。 delegates.put(A.class, new ISimpleDelegate() { //write your logic here });

          希望思路清晰

          在您的循环中,您只需调用代表:

          while(iterator.hasNext()) {
              Base next = iterator.next();
              delegates.get(next.getClass()).doSomeLogic(next);
          }
          

          【讨论】:

            【解决方案5】:

            一般来说,避免instanceof 的一个很好的解决方案是使用所谓的visitor pattern

            对于这种模式,您需要一个额外的接口(访问者)、一个包含您要执行的代码的实现以及层次结构的所有类中的一个额外的方法,因此在小情况下这可能是矫枉过正(但如果不仅有AB,还有更多类型,那就很方便了。

            在你的情况下,它看起来像这样:

            interface Visitor {
              void visit(A a);
              void visit(B b);
            }
            
            class Base {
              abstract accept(Visitor v);
            }
            
            class A extends Base {
              accept(Visitor v) {
                v.visit(this);
              }
            }
            
            class B extends Base {
              accept(Visitor v) {
                v.visit(this);
              }
            }
            
            class MyVisitor implements Visitor {
              visit(A a) {
                doSomethingWithA(a);
              }
            
              visit(B b) {
                doSomethingWithB(b);
              }
            }
            

            它是这样使用的:

            MyVisitor v = new MyVisitor();
            while(iterator.hasNext()) {
                Base next = iterator.next();
                next.accept(v);
            }
            

            一个优点是您只需编写大部分代码一次。如果你想在程序的另一个地方用 A 和 B 做其他事情,只需编写另一个访问者的实现。您无需像将doSomething() 添加到这些类中那样修改BaseAB

            编辑: 如果子类的数量增加,您需要更改所有现有的Visitor 实现。但是,至少编译器会告诉您这一点。使用instanceof,您最终可能会忘记需要添加处理子句的地方。这最多可以在运行时检测到,而访问者模式为您提供编译时安全性。

            【讨论】:

              猜你喜欢
              • 1970-01-01
              • 1970-01-01
              • 2021-04-01
              • 2020-07-21
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              • 1970-01-01
              相关资源
              最近更新 更多