让小兄弟做一些新的技术研究,他兴奋地用上了垂涎已久的 Kotlin 语言,并大加赞赏。其中有个场景是,让他设计一套图像特效的接口与实现。
他用 Kotlin 欢快地写成了这样:
1 2 3 |
interface Effect { fun apply(input: Bitmap) : Bitmap } |
并以之为基础接口,至少实现了三个特效。
我在 review 的时候哭笑不得,因为仍然把我提醒过的选项参数给丢掉了,今天我想修复一下。最简单的办法我是知道的,直接向上述 apply
方法中增加一个参数,然后再把那三个实现相应调整过来,再把相关调用也调整过来即可。
但这不符合我作为一个“勤奋的懒人”的直觉。我的第一目标通常是,尽可能少地修改供给侧,尽量不修改消费侧。是否可以新增一个接口方法,原型上加入选项参数,然后将当前这一接口方法改为对新方法的将选型参数置为空的调用呢?经过摸索,以下代码出炉:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
interface Effect { class Options { companion object { val default: Options = Options() } } fun apply(input: Bitmap) : Bitmap { return apply(input, Options.default) } fun apply(input: Bitmap, options: Options) : Bitmap } |
然后再去各个具体实现类中把函数签名中加入 options: Effect.Options
这一新的参数即可。点击编译,顺利通过。作为一个 Kotlin 菜鸟,真是要乐出花来。
然而收获胡萝卜结束不久,大棒随之而来。由于我是“一个 Kotlin 菜鸟”,所以在我想新写一个具体特效时,我决定还是使用 Java(主要原因是现成代码会多一些)。我开开心心地写下如下代码后,惊讶地发现,IDE 提示该类对于派生而来的接口的实现不够完整:
1 2 3 4 5 |
public class ShadowEffect implements Effect { public Bitmap apply(Bitmap input, Effect.Options options) { return null; } } |
Java 需要同时实现两个接口函数,成为这样:
1 2 3 4 5 6 7 8 9 |
public class ShadowEffect implements Effect { public Bitmap apply(Bitmap input) { return apply(input, null); } public Bitmap apply(Bitmap input, Effect.Options options) { return null; } } |
很显然,在 Kotlin 中,interface
同时接纳了 Java 的 abstract
类的部分特性,允许带有具有实现代码的接口函数存在;而 Java 对派生而来的 Kotlin 接口,只接纳其接口函数声明部分,对其具有的实现予以了摒弃。也就是说,将来的其它实现了 Effect
接口的 Java 类,也都要提供自己版本的 Bitmap apply(Bitmap input)
实现,不能从 Kotlin 的现成 fun apply(input: Bitmap) : Bitmap
实现中受益。
如果为了这样一个共同实现的函数的复用,一个可选的(但是笨拙的)方法是,提供一个新的 Java 基类,如下:
1 2 3 4 5 |
public abstract class JavaEffect implements Effect { public Bitmap apply(Bitmap input) { return apply(input, null); } } |
然后将具体实现类的基类调整为它,并把 implements
转换为 extends
。当然,在某种程度上这是令人沮丧的,毕竟抽象类跟接口不是一回事。