使用泛型的类调用错误的方法版本(超类而不是子类)?

时间:2015-03-18 19:22:42

标签: java generics

我正在尝试重构一些代码,将一些缓存中共享的公共代码移动到基类。这是一个简化的概念:

public abstract class DBObject {
    public void copyTo(DBObject other) {
       other.setId(this.id);
    }
} 

public class Person extends DBObject {
    public void copyTo(Person other) {
       super.copyTo(other);
       other.setName(this.name);
    }
} 

public class PersonCache extends Cache<Person> {
}

public abstract class Cache<T extends DBObject>  {
    Map<Long, T> idToCachedMap;
    private Class<T> tObjectClass;

    public void initialize() {
        // does stuff to populate the idToCachedMap
    }

    public void updateCache(T cachedObjToUpdate) {
        T cachedObj = idToCachedMap.get(cachedObjToUpdate.getId());
        T oldCachedObj = tObjectClass.newInstance();;

        cachedObj.copyTo(oldCachedObj); // PROBLEM HERE
        // do other stuff...
    }
}

我遇到的问题是,当我在PersonCache上调用updateCache(Person)时,在对象上调用的copyTo方法是DBObject中的方法,而不是Person中的方法。因此,实际上只复制了一些数据(在本例中为ID,但不是名称)。

在我看来,由于cachedObj和oldCachedObjs都是Person对象(如果它是PersonCache),因此应调用的copyTo方法是Person类中的方法。

我觉得必须有一些关于我所缺少的仿制药如何工作导致这种行为。我知道如果我将Person类中的copyTo覆盖为copyTo(DBObject other)的签名而不是copyTo(Person other),那么它会在person类上调用copyTo - 但这是一种重写的方式它,我想我错过了一些可能更清洁的东西。

2 个答案:

答案 0 :(得分:3)

您没有覆盖copyTo方法,因为您更改了签名。并且您调用T中存在的方法。

试试这个:

public abstract class DBObject<T extends DBObject> {
  public void copyTo(T other) {
    other.setId(this.id);
  }
} 

public class Person extends DBObject<Person> {
  @Override
  public void copyTo(Person other) {
   super.copyTo(other);
   other.setName(this.name);
  }
} 

答案 1 :(得分:2)

你陈述在我看来,既然cachedObj和oldCachedObjs都是Person对象,如果它是PersonCache,那么应该调用的copyTo方法就是Person类中的那个。

由于type erasure这是一个不正确的假设,在运行时它知道的所有内容都是DBObject,显然也是Object

它在运行时对T一无所知,它已擦除并且在运行时不可用。

由于类型擦除,

copyTo(T other) copyTo(Person other) 重载而非重写等效。 copyTo(T other)实际上变为copyTo(DBObject other),因为您的行为表明它与copyTo(DBObject other)匹配。这是预期的行为。

类型擦除行为在SO和互联网上都有很好的记录。