首页 > 代码库 > 我想要的程序开发语言特性——之“面向对象”——之“退化”
我想要的程序开发语言特性——之“面向对象”——之“退化”
先从一个例子开始讲起,以下是jdk1.7中的迭代器接口的代码(去掉了注释的部分):
public interface Iterator<E> {
boolean hasNext();
E next();
void remove();
}
程序开发的老油条们都不太喜欢这个接口的remove方法,原因可能是:
- 我们为自己实现Iterator接口时,基本不需要这个方法,但我们却不得不@Override它,此时通常我们会直接在方法体中写一行throw new UnsupportedOperationException();来搞定它。
- 这个方法的性能通常不太好,对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),我们继承了抽象类之后就没有继承其它类的可能性了。
或许我们可以考虑在一门语言中实现一个“退化”的语法,这样就可以:
- 实现接口或类时可以从某接口或类中去掉一部分方法
- 在某些对象中去掉一部分方法。
在一些动态类型语言中已经有这样的功能了,比如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开始已经在某种程度上打破了。
从设计模式的角度来看,对类或接口的退化能力似乎又违反“里氏替换原则”,不过既然一开始就是这错误,而且退化能力可以在一定程度上抹掉“先辈的罪”,那么有时候也不要太讲原则了。