Nekostack

Golang, Swift, Vim, Cat, Curry, Whisky.

ジェネリックなイニシャライザが継承されない

Posted on 14 January 2016.

Swift のジェネリクスとイニシャライザ絡みについてメモ。 Automatic initializer inheritance のルールは逸脱していないのに、 なぜか継承されないと思ったら、 どうもそういうバグらしい。

[SR-416] Automatic initializer inheritance doesn’t work for generic types.

Automatic initializer inheritance

イニシャライザが自動的に継承されるかどうかは、 以下の二つのルールに従う仕様になっている。

Rule 1

If your subclass doesn’t define any designated initializers, it automatically inherits all of its superclass designated initializer.

Rule 2

If your subclass provides an implementation of all of its superclass designated initializers — either by inheriting them as per rule 1, or by providing a custom implementation as part of its definition — then it automatically inherits all of the superclass convenience initializers.

Automatic Initializer Inheritance - The Swift Programming Language

例えば、サブクラスで一切イニシャライザを定義していなければ、 上記二つのルールはみたされ、 デシグネイトイニシャライザとコンビニエンスイニシャライザ全てが自動的に継承される。

上のコードは、クラス Nodeinit(value: String) を定義していて、 Node の子クラス Terminal でイニシャライザを一切定義しなかった場合。 Rule 1 則って init(value: String) が子クラスで自動的に継承されるという、 他の言語からしてもごくごく違和感ない動きをする。

ジェネリックなクラスになった場合もそのように動いてしかるべきだと思いきや、 (ルールを見てもそうなっていて良いはずだが) 現状そうなってない。

ジェネリックなクラスの継承

たとえば、Node<T> というクラスと、 それを継承した Terminal<T> というクラスを実装した場合を考える。

パラメーターの型以外は先ほどと同様に、クラス Node<T>init(value: T) を定義し、 Node<T> を継承した Terminal<T> をさらに定義している。 この場合、Terminal<T>init(value: T) を自動的に継承していることを期待するが、 コンパイラからはイニシャライザが見えずエラーを吐く。

暫定対処

手動で対処。イニシャライザーをオーバーライドしてしまうなど。

バグトラッカーで優先度が Medium となっているので、 もしかするとそのうち修正されるのかもしれない。