Open9
言語によって引数の不変・反変・共変が違う件
ピン留めされたアイテム
README
言語ごとに引数が不変、反変、共変のどれかを調べてます。
まとめ
言語 | 不変 | 反変 | 共変 |
---|---|---|---|
PHP | ✅ | ✅ | ❌ |
Go | ✅ | ❌ | ❌ |
Java | ✅ | ❌ | ❌ |
Scala | ✅ | ✅ | ❌ |
Kotlin | ✅ | ✅ | ❌ |
TypeScript (strictFunctionTypes: true + 関数) | ✅ | ✅ | ❌ |
TypeScript (strictFunctionTypes: true + メソッド) | ✅ | ✅ | ✅ |
TypeScript (strictFunctionTypes: false + 関数) | ✅ | ✅ | ✅ |
TypeScript (strictFunctionTypes: false+ メソッド) | ✅ | ✅ | ✅ |
Dart | ✅ | ✅ | ❌ |
Swift(関数) | ✅ | ✅ | ❌ |
Swift(インターフェイス) | ✅ | ❌ | ❌ |
型の健全性としては、リスコフの置換原則的に共変でないほうが良さそう
言語 | 不変 | 反変 | 共変 |
---|---|---|---|
健全な言語 | ✅ | ✅/❌ | ❌ |
PHPは反変
version: 8.1.0
言語 | 不変 | 反変 | 共変 |
---|---|---|---|
PHP | ✅ | ✅ | ❌ |
class Animal {}
class Bird extends Animal {}
class Chiken extends Bird {}
interface BirdWatchable {
function watch(Bird $bird);
}
class BirdWatcher implements BirdWatchable {
// 不変
public function watch(Bird $bird) {}
}
class AnimalWatcher implements BirdWatchable {
// 反変
public function watch(Animal $animal) {}
}
class ChikenWatcher implements BirdWatchable {
// 共変
public function watch(Chiken $chiken) {}
// Fatal error: Declaration of ChikenWatcher::watch(Chiken $chiken) must be compatible with BirdWatchable::watch(Bird $bird)
}
Goは不変
version: 1.17.5
言語 | 不変 | 反変 | 共変 |
---|---|---|---|
Go | ✅ | ❌ | ❌ |
package main
type Animal interface {
Animal()
}
type Bird interface {
Animal
Bird()
}
type Chiken interface {
Bird
Chiken()
}
type BirdWatchable interface {
Watch(bird Bird)
}
// implements BirdWatchable
type AnimalWatcher struct {}
func (aw *AnimalWatcher) Watch (animal Animal) {}
// implements BirdWatchable
type BirdWatcher struct {}
func (aw *BirdWatcher) Watch (bird Bird) {}
// implements BirdWatchable
type ChikenWatcher struct {}
func (cw *ChikenWatcher) Watch (chiken Chiken) {}
// 不変
var bw BirdWatchable = (*BirdWatcher)(nil)
// 反変
var aw BirdWatchable = (*AnimalWatcher)(nil)
// cannot use (*AnimalWatcher)(nil) (type *AnimalWatcher) as type BirdWatchable in assignment:
// *AnimalWatcher does not implement BirdWatchable (wrong type for Watch method)
// have Watch(Animal)
// want Watch(Bird)
// 共変
var cw BirdWatchable = (*ChikenWatcher)(nil)
// cannot use (*ChikenWatcher)(nil) (type *ChikenWatcher) as type BirdWatchable in assignment:
// *ChikenWatcher does not implement BirdWatchable (wrong type for Watch method)
// have Watch(Chiken)
// want Watch(Bird)
func main() {}
Javaは不変
version: 1.8.0_282
言語 | 不変 | 反変 | 共変 |
---|---|---|---|
Java | ✅ | ❌ | ❌ |
class Untitled {
public static void main(String[] args) {
}
}
class Animal {}
class Bird extends Animal {}
class Chiken extends Bird {}
interface BirdWatchable {
public void watch(Bird bird);
}
class BirdWatcher implements BirdWatchable {
// 不変
public void watch(Bird bird) {}
}
class AnimalWatcher implements BirdWatchable {
// 反変
public void watch(Animal animal) {}
// エラー: AnimalWatcherはabstractでなく、BirdWatchable内のabstractメソッドwatch(Bird)をオーバーライドしません
}
class ChikenWatcher implements BirdWatchable {
// 共変
public void watch(Chiken chiken) {}
// エラー: ChikenWatcherはabstractでなく、BirdWatchable内のabstractメソッドwatch(Bird)をオーバーライドしません
}
Scalaは反変
version: 3.1.0
言語 | 不変 | 反変 | 共変 |
---|---|---|---|
Scala | ✅ | ✅ | ❌ |
class Animal
class Bird extends Animal
class Chiken extends Bird
// 不変
val watchBird: (Bird) => Unit = (bird: Bird) => {}
// 反変
val watchAnimal: (Bird) => Unit = (animal: Animal) => {}
// 共変
val watchChiken: (Bird) => Unit = (chiken: Chiken) => {}
// Found: Playground.Chiken => Unit
// Required: Playground.Bird => Unit
Kotlinは反変
version: 1.6.10
言語 | 不変 | 反変 | 共変 |
---|---|---|---|
Kotlin | ✅ | ✅ | ❌ |
open class Animal
open class Bird : Animal()
class Chiken : Bird()
// 不変
val f1: (Bird) -> Unit = fun (bird: Bird) {}
// 反変
val f2: (Bird) -> Unit = fun (animal: Animal) {}
// 共変
val f3: (Bird) -> Unit = fun (chiken: Chiken) {}
// Type mismatch: inferred type is (Chiken) -> Unit but (Bird) -> Unit was expected
// Expected parameter of type Bird
fun main() {}
TypeScriptはときどき双変
version: 4.5.3
言語 | 不変 | 反変 | 共変 |
---|---|---|---|
TypeScript (strictFunctionTypes: true + 関数) | ✅ | ✅ | ❌ |
TypeScript (strictFunctionTypes: true + メソッド) | ✅ | ✅ | ✅ |
TypeScript (strictFunctionTypes: false + 関数) | ✅ | ✅ | ✅ |
TypeScript (strictFunctionTypes: false+ メソッド) | ✅ | ✅ | ✅ |
strictFunctionTypes
がtrue
のとき
コンパイラオプション// @strictFunctionTypes: true
class Animal {
#thisTypeIsNominal: any;
}
class Bird extends Animal {
#thisTypeIsNominal: any;
}
class Chiken extends Bird {
#thisTypeIsNominal: any;
}
// 不変
const f1: (bird: Bird) => void = (bird: Bird) => {};
// 反変
const f2: (bird: Bird) => void = (animal: Animal) => {};
// 共変
const f3: (bird: Bird) => void = (chiken: Chiken) => {};
// Type '(animal: Chiken) => void' is not assignable to type '(bird: Bird) => void'.
interface BirdWatchable {
watch(bird: Bird): void;
}
class BirdWatcher implements BirdWatchable {
// 不変
watch(bird: Bird): void {}
}
class AnimalWatcher implements BirdWatchable {
// 反変
watch(animal: Animal): void {}
}
class ChikenWatcher implements BirdWatchable {
// 共変
watch(chiken: Chiken): void {}
}
strictFunctionTypes
がfalse
のとき
コンパイラオプション// @strictFunctionTypes: false
class Animal {
#thisTypeIsNominal: any;
}
class Bird extends Animal {
#thisTypeIsNominal: any;
}
class Chiken extends Bird {
#thisTypeIsNominal: any;
}
// 不変
const f1: (bird: Bird) => void = (bird: Bird) => {};
// 反変
const f2: (bird: Bird) => void = (animal: Animal) => {};
// 共変
const f3: (bird: Bird) => void = (chiken: Chiken) => {};
interface BirdWatchable {
watch(bird: Bird): void;
}
class BirdWatcher implements BirdWatchable {
// 不変
watch(bird: Bird): void {}
}
class AnimalWatcher implements BirdWatchable {
// 反変
watch(animal: Animal): void {}
}
class ChikenWatcher implements BirdWatchable {
// 共変
watch(chiken: Chiken): void {}
}
Dartは反変
version: 2.15.0
言語 | 不変 | 反変 | 共変 |
---|---|---|---|
Dart | ✅ | ✅ | ❌ |
void main() {}
class Animal {}
class Bird extends Animal {}
class Chiken extends Bird {}
typedef BirdFunc = void Function(Bird bird);
// 不変
BirdFunc f1 = (Bird bird) => {};
// 反変
BirdFunc f2 = (Animal animal) => {};
// 共変
BirdFunc f3 = (Chiken chiken) => {};
// Error: A value of type 'void Function(Chiken)' can't be assigned to a variable of type 'void Function(Bird)'.
abstract class BirdWatchable {
void watch(Bird bird);
}
class BirdWatcher implements BirdWatchable {
// 不変
void watch(Bird bird) {}
}
class AnimalWatcher implements BirdWatchable {
// 反変
void watch(Animal animal) {}
}
class ChikenWatcher implements BirdWatchable {
// 共変
void watch(Chiken chiken) {}
// Error: The parameter 'chiken' of the method 'ChikenWatcher.watch' has type 'Chiken', which does not match the corresponding type, 'Bird', in the overridden method, 'BirdWatchable.watch'.
}
Swiftはプロトコルは不変、関数は反変
version: ???
言語 | 不変 | 反変 | 共変 |
---|---|---|---|
Swift(関数) | ✅ | ✅ | ❌ |
Swift(インターフェイス) | ✅ | ❌ | ❌ |
class Animal {}
class Bird: Animal {}
class Chiken: Bird {}
func useBird(a: Bird) -> Void {}
func useAnimal(a: Animal) -> Void {}
func useChiken(a: Chiken) -> Void {}
// 不変
let f1: (Bird) -> Void = useBird
// 反変
let f2: (Bird) -> Void = useAnimal
// 共変
let f3: (Bird) -> Void = useChiken
// Cannot convert value of type '(Chiken) -> Void' to specified type '(Bird) -> Void'
protocol BirdWatchable {
func watch(a: Bird) -> Void
}
class BirdWatcher: BirdWatchable {
// 不変
func watch(a: Bird) -> Void {}
}
class AnimalWatcher: BirdWatchable {
// 反変
func watch(a: Animal) -> Void {}
// Type 'AnimalWatcher' does not conform to protocol 'BirdWatchable'
}
class ChikenWatcher: BirdWatchable {
// 共変
func watch(a: Chiken) -> Void {}
// Type 'ChikenWatcher' does not conform to protocol 'BirdWatchable'
}