这里的instanceof检查有什么问题吗?

4

随着泛型的引入,我尽可能地不想执行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检查,是否有其他方法可以做到这一点?
谢谢

1
有没有办法利用多态呢?比如在 MyObjectOtherObject 中都有一个 doFurtherProcessing() 方法,可以做正确的事情吗? - NullUserException
我怀疑一开始需要这样做就是问题所在。为什么这些对象一开始会混在一起呢? - Louis Wasserman
3个回答

2

你可以使用双重调用。不能保证这是更好的解决方案,但它是一种替代方法。

代码示例

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对象的重载方法中。调用哪个方法取决于该对象的类型。

另请参见访问者模式。我的代码示例不够好,因为ICacheable.put(...)方法缺乏内聚性,但使用访问者模式中定义的接口可以解决这个问题。

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

在Java中,覆盖始终绑定运行时,但重载要复杂一些:动态分派意味着方法的实现将在运行时选择,但方法的签名仍然在编译时确定。(查看Java语言规范第8.4.9章获取更多信息,还可以查看书籍Java Puzzlers第137页上的难题“Making a Hash of It”)。

1

有没有办法将每个映射中的缓存对象合并到一个映射中?它们的键可以保持分离,因此您可以将它们存储在一个映射中。如果您无法这样做,那么您可以有一个

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”的一个很好的通用替代方案。 - Richard JP Le Guen
关键词是“工作”。但需要记住的一件事是,与更通用的解决方案不同,如果您添加ICacheable的新实现者,则不需要进行任何更改。从某种意义上说,尽管双重分派是使用instanceof的通用解决方案,但这更加灵活。 - chubbsondubs

0
您可以执行以下操作:
  1. 向您的ICachableInterface接口添加一个方法,该方法将处理将对象放入作为参数提供给该方法的两个Map之一中。
  2. 在您的两个实现类中实现此方法,使每个类决定将自己放入哪个Map中。
  3. 从您的for循环中删除instanceof检查,并将put方法替换为调用步骤1中定义的新方法。

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


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接