【问题标题】:Anything wrong with instanceof checks here?这里的 instanceof 检查有什么问题吗?
【发布时间】:2023-04-10 22:40:01
【问题描述】:

随着泛型的引入,我不愿意尽可能多地执行 instanceof 或强制转换。但在这种情况下我看不到解决办法:

for (CacheableObject<ICacheable> cacheableObject : cacheableObjects) {
    ICacheable iCacheable = cacheableObject.getObject();
    if (iCacheable instanceof MyObject) {
        MyObject myObject = (MyObject) iCacheable;
        myObjects.put(myObject.getKey(), myObject);
    } else if (iCacheable instanceof OtherObject) {
        OtherObject otherObject = (OtherObject) iCacheable;
        otherObjects.put(otherObject.getKey(), otherObject);
    }
}

在上面的代码中,我知道我的 ICacheables 应该只是 MyObject 或 OtherObject 的实例,并且根据这一点,我想将它们放入 2 个单独的映射中,然后进一步执行一些处理。

如果没有我的 instanceof 检查,如果有其他方法可以做到这一点,我会很感兴趣。

谢谢

【问题讨论】:

  • 有没有办法利用多态性?比如,在MyObjectOtherObject 中都有一个doFurtherProcessing() 方法做正确的事?
  • 我怀疑首先需要这样做是问题所在。为什么这些物体首先会混在一起?

标签: java oop


【解决方案1】:

您可以使用双重调用。没有承诺这是一个更好的解决方案,但它是一个替代方案。

代码示例

import java.util.HashMap;

public class Example {

    public static void main(String[] argv) {
        Example ex = new Example();
        ICacheable[] cacheableObjects = new ICacheable[]{new MyObject(), new OtherObject()};

        for (ICacheable iCacheable : cacheableObjects) {
            // depending on whether the object is a MyObject or an OtherObject,
            // the .put(Example) method will double dispatch to either
            // the put(MyObject) or  put(OtherObject) method, below
            iCacheable.put(ex);
        }

        System.out.println("myObjects: "+ex.myObjects.size());
        System.out.println("otherObjects: "+ex.otherObjects.size());
    }

    private HashMap<String, MyObject> myObjects = new HashMap<String, MyObject>();
    private HashMap<String, OtherObject> otherObjects = new HashMap<String, OtherObject>();

    public Example() {

    }

    public void put(MyObject myObject) {
        myObjects.put(myObject.getKey(), myObject);
    }

    public void put(OtherObject otherObject) {
        otherObjects.put(otherObject.getKey(), otherObject);
    }

}

interface ICacheable {
    public String getKey();
    public void put(Example ex);
}

class MyObject implements ICacheable {

    public String getKey() {
        return "MyObject"+this.hashCode();
    }

    public void put(Example ex) {
        ex.put(this);
    }
}

class OtherObject implements ICacheable {

    public String getKey() {
       return "OtherObject"+this.hashCode();
    }

    public void put(Example ex) {
        ex.put(this);
    }

}

这里的想法是-而不是强制转换或使用instanceof-您调用iCacheable对象的.put(...)方法,该方法将自身传递回Example对象的重载方法。调用哪个方法取决于该对象的类型。

另请参阅Visitor pattern。我的代码示例有异味,因为 ICacheable.put(...) 方法没有凝聚力 - 但是使用访问者模式中定义的接口可以消除这种异味。

为什么我不能直接从 Example 类中调用 this.put(iCacheable)

在 Java 中,重写总是在运行时绑定,但重载稍微复杂一些:动态调度意味着方法的实现将在运行时选择,但方法的签名仍然在编译时确定。 (查看Java Language Specification, Chapter 8.4.9 了解更多信息,还可以查看Java Puzzlers 本书第137 页上的益智游戏“Making a Hash of It”。)

【讨论】:

    【解决方案2】:

    有没有办法将每张地图中的缓存对象合并为一张地图?他们的密钥可以将它们分开,因此您可以将它们存储在一张地图中。如果你不能这样做,那么你可以有一个

    Map<Class,Map<Key,ICacheable>>
    

    然后这样做:

    Map<Class,Map<Key,ICacheable>> cache = ...;
    
    public void cache( ICacheable cacheable ) {
       if( cache.containsKey( cacheable.getClass() ) {
          cache.put( cacheable.getClass(), new Map<Key,ICacheable>() );
       }
       cache.get(cacheable.getClass()).put( cacheable.getKey(), cacheable );
    }
    

    【讨论】:

    • 这适用于这个特定的实例,但不是使用 instanceof 的一个很好的通用替代方案。
    • 有效的词是works。但是,要记住的一件事是,如果您添加一个新的 ICacheable 实现者,它不需要任何更改,这与更通用的解决方案不同。在某种程度上,即使双重调度是使用 instanceof 的通用解决方案,这也更加灵活。
    【解决方案3】:

    您可以执行以下操作:

    1. 向您的ICachableInterface 接口添加一个方法,该方法将处理将对象放入两个地图之一,作为该方法的参数。
    2. 在您的两个实现类中的每一个中实现此方法,让每个类决定将自己放入哪个 Map。
    3. 删除 for 循环中的 instanceof 检查,并将 put 方法替换为对步骤 1 中定义的新方法的调用。

    然而,这不是一个好的设计,因为如果您有另一个实现此接口的类和第三个映射,那么您需要将另一个映射传递给您的新方法。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-10-03
      • 1970-01-01
      • 2010-09-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多