弃用方法但保留功能

3

假设我有一个包含抽象类和抽象方法的库:

public abstract class MyAbstractClass{

    public void myMethod(){
        int a = doSomething("hi");
    }

    public abstract void doSomething(String param);
}

现在,我决定给这个方法添加一个参数,但是我想保留旧方法的功能以便让旧代码可用:
public void myMethod(){
    int a = ?
}

/**
 * @deprecated use doSomething(String, String) instead.
 */
@Deprecated
public int doSomething(String param){ return doSomething(param, null); }

public abstract int doSomething(String param, String secondParam);

在这种情况下,我该如何实现myMethod


Android支持库中的PagerAdapter类实际上有一种类似于这样的结构,但是方向相反:

public Object instantiateItem(ViewGroup container, int position) {
    return instantiateItem((View) container, position);
}

/**
 * @deprecated Use {@link #instantiateItem(ViewGroup, int)}
 */
public Object instantiateItem(View container, int position) {
    throw new UnsupportedOperationException(
            "Required method instantiateItem was not overridden");
}

这种行为是应该被不鼓励的吗?如果我要使用这种构造,怎样才能知道调用哪个方法呢?


4
旧代码如何可用?您引入了一个新的抽象方法,打破了所有现有的子类。 - JB Nizet
4
为什么不能继续调用doSomething("hi")(带有抑制警告)或者仅调用doSomething("hi", null) - Jon Skeet
您可以在 myMethod() 中调用新的 doSomething(String, String) - shuangwhywhy
如果有子类是无法修改的,那么添加一个新的抽象方法将会导致在子类实例上每次调用这个新方法时出现异常。当2个参数的方法不存在时,您想要做什么?调用仅带一个参数的方法(从而忽略第二个参数),还是抛出异常? - JB Nizet
那么第二个参数确实可以被忽略(第二个参数是为了提供一个选项以获得更好的性能)。 - nhaarman
显示剩余6条评论
2个回答

3

我想我明白了你的困境。你在一个库中有一个抽象类,人们正在进行子类化并实现它的抽象方法,你想要弃用这个方法并添加一个新的抽象方法,从现在开始应该实现这个新方法。

这是我会做的:

之前

从一个Feature类开始,你的库的用户正在对其进行子类化。

public abstract class Feature {
    public abstract void doSomething(String param);
}

之后

保持 Feature 类基本不变,但是将该方法废弃,并在文档中宣传人们应该现在继承 NewFeature 而不是 Feature 并在该类中实现全新抽象方法。已经继承 Feature 的现有代码仍然可以正常使用。

public abstract class Feature {
    /**
      @deprecated Extend NewFeature instead and implement doSomething(a, b) 
    */
    @Deprecated
    public abstract void doSomething(String param);
}

public abstract class NewFeature extends Feature {

    @Deprecated
    @Override
    public void doSomething(String param) {
        doSomething(param, null);
    }

    public abstract void doSomething(String param, String paramTwo);
}

更进一步的未来

足够长的时间过去后,您可以删除 Feature 类。例如,我认为Spring倾向于在首次宣布弃用之后一个完整版本中删除方法。


这看起来确实是理想的方法。我可以检查该类是否是 NewFeature 的子类来决定调用哪种方法。事实上,在 NewFeaturedoSomething() 方法中,我不需要调用 doSomething(param, null),因为它永远不会被调用,对吗? - nhaarman
当然可以 - 任何有意义的方式都可以。没有具体的例子很难说。 - theon

0

根据评论,这是我会做的事情:

public void myMethod(){
    int a = doSomething("hi", "theOptimalSecondArgumentValue");
}

/**
 * @deprecated use doSomething(String, String) instead.
 */
@Deprecated
public abstract int doSomething(String param);

/**
 * Delegates to {@link #doSomething(String)} and thus ignores the second argument 
 * by default. Subclasses should override this method to return a better result,
 * taking the second argument into account
 */
public int doSomething(String param, String secondParam) {
    return doSomething(param);
}

现有的子类仍然可以工作,但会处于“降级”模式,第二个参数始终被忽略。

新的子类将简单地按照以下方式实现:

@Override
public int doSomething(String param) {
    doSomething(param, "theOptimalDefaultValue");
}

@Override
public int doSomething(String param, String secondParam) {
    // compute the result using the two arguments
}

虽然这样做可以起作用,但我认为它会给实现者增加一些困惑。弃用该方法,但仍需要覆盖它。 - nhaarman

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