首页 > 代码库 > 我想要的程序开发语言特性——之“面向对象”——之“退化”

我想要的程序开发语言特性——之“面向对象”——之“退化”

先从一个例子开始讲起,以下是jdk1.7中的迭代器接口的代码(去掉了注释的部分):


public interface Iterator<E> {
    boolean hasNext();
    E next();
    void remove();
}


程序开发的老油条们都不太喜欢这个接口的remove方法,原因可能是:

  1. 我们为自己实现Iterator接口时,基本不需要这个方法,但我们却不得不@Override它,此时通常我们会直接在方法体中写一行throw new UnsupportedOperationException();来搞定它。
  2. 这个方法的性能通常不太好,对ArrayList的迭代器频繁的做这个操作的性能远不如创建一个新的ArrayList对象。

 

或许,这个接口没有remove方法会更好,但这个接口已经这样了,要保持向上兼容的话,就不可能修改jdk去掉这个方法。

那么我们在不改变原有类的情况下,如何“退化”掉原有类中的方法呢?

 

当我们自己的类需要迭代器时,我们是否可以直接使用自己的接口,而不使用jdk自带的接口呢:


public interface MyIterator<E> {
    boolean hasNext();
    E next();
}


这样当然是可以的,但这样就无法直接适配到jdk自带的集合框架了。

所以通常我们会这样做:


class MyIteratorImp implements Iterator<Object> {
    private Iterator<Object> itr;
    public MyIteratorImp(Iterator<Object> itr){
        this.itr = itr;
    }
    public boolean hasNext() {
        return itr.hasNext();
    }
    public Object next() {
        return itr.next();
    }
    public void remove() {
       throw new UnsupportedOperationException();
    }
}


这里我们根本不需要remove方法,但我们不得不去实现它,因为这是Java的语法(这样实现多少会让人有点不爽)。

我们可能非常希望实现这样一个接口:


interface MyIterator<T> extends Iterator<T> {
    不要这个方法: void remove();
}


这样,我们实现自己的迭代器时不需要再override remove方法,同时又是jdk标准接口Iterator的实现,这样基本上满足了我们要求。

不过遗憾的是,我们没有这样的语法。

 

或许有人认为这样做也是可以的(使用抽象类):


abstract class MyIteratorImp<T> implements Iterator<T> {
    public void remove() {
        throw new UnsupportedOperationException();
    }
}


但抽象类相对于接口却有一个缺点:在一些不支持多重继承的语言中(例如Java),我们继承了抽象类之后就没有继承其它类的可能性了。

 

或许我们可以考虑在一门语言中实现一个“退化”的语法,这样就可以:

  1. 实现接口或类时可以从某接口或类中去掉一部分方法
  2. 在某些对象中去掉一部分方法。

在一些动态类型语言中已经有这样的功能了,比如Groovy和Python。

上面的第1点其实并非必须是动态语言才能实现的(但到现在我还没有看到哪个静态类型语言有这样的功能),但第2点就一定是动态语言才行了(因为有些情况下,不到运行时,我们无法确定对象会什么时候被创建出来,甚至创建对象的代码我们都不知道在哪里,我们只是使用这个对象)。

 

Java进化到Java8之后,有了一个不错的选择:接口默认实现。

实际上Jdk1.8中的Iterator接口的实现已经是这个样子了:


public interface Iterator<E> {
    boolean hasNext();
    E next();
    default void remove() {
        throw new UnsupportedOperationException("remove");
    }
    default void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        while (hasNext())
            action.accept(next());
    }
}


接口默认实现使“接口”在很大程度上变成了“抽象类”——Java本来不支持的多重继承在Java8开始已经在某种程度上打破了。

从设计模式的角度来看,对类或接口的退化能力似乎又违反“里氏替换原则”,不过既然一开始就是这错误,而且退化能力可以在一定程度上抹掉“先辈的罪”,那么有时候也不要太讲原则了。