У наступному прикладі f3 може прийняти Iterable [Array [Int]]
def f3(o:Iterable[Iterable[Any]]):Unit = {}
f3(Iterable(Array(123))) // OK. Takes Iterable[Array[Int]]
але якщо я призначу Iterable [масив [Int]] локальній змінній, він не може:
val v3 = Iterable(Array(123))
f3(v3) // Fails to take Takes Iterable[Array[Int]]
з помилкою:
Error:(918, 10) type mismatch;
found : Iterable[Array[Int]]
required: Iterable[Iterable[Any]]
f3(x)
Що за нісенітниця? Чому перший приклад працює, а не секунди. Здається, це має щось спільне з вкладеними дженериками:
def f(o:Iterable[Any]):Unit = {}
f( Array(123))
val v1 = Array(123)
f(v1) // OK
def f2(o:Iterable[Any]):Unit = {}
f2( Iterable(Array(123)))
val v2 = Array(123)
f(v2) // OK
З шкалою.2.11
Відповіді:
6 за відповідь № 1Перш за все, це важливо Array
не поширюється Iterable
(тому що це тип Java). Натомість відбувається неявна конверсія з Array[A]
до Iterable[A]
, тому очікувані типи мають значення.
У першому випадку: Iterable(Array(123))
є аргументом до f3
і, таким чином, перевіряється тип очікуваного типу Iterable[Iterable[Any]]
. Так Array(123)
перевіряється очікуваним типом Iterable[Any]
. Ну, його фактичний тип Array[Int]
і компілятор вставляє перетворення (тому що Iterable[Int]
відповідає Iterable[Any]
). Так це насправді Iterable(array2iterable(Array(123))
(Я не пам'ятаю точну назву).
У другому випадку f3
має тип Iterable[Array[Int]]
: нічого не може викликати неявну конверсію в val f3 = ...
лінія, правда? І немає неявного перетворення з Iterable[Array[Int]]
до Iterable[Iterable[Int]]
(або, загалом, від Iterable[A]
до Iterable[B]
коли є неявна конверсія з A
до B
), тому наступний рядок не вдалося скласти. Ви можете написати це перетворення самостійно, але це не допоможе, наприклад, перетворити Array[Array[Int]]
до Iterable[Iterable[Int]]
.
І звичайно, якщо ви користуєтеся Iterable[Any]
, знову немає нічого, що може викликати неявну конверсію!
4 для відповіді № 2
Це пов'язано з тим, як працює висновок / об'єднання типу Scala.
Коли ви визначаєте змінну і виключаєте тип, Scala застосовує найбільш можливий тип:
scala> val v1 = Iterable(Array(123))
v1: Iterable[Array[Int]] = List(Array(123))
Однак, коли ви вказуєте очікуваний тип (наприклад, передаючи значення функції з визначеним типом параметра), Scala уніфікує даний параметр із очікуваним типом (якщо можливо):
scala> val v2 : Iterable[Iterable[Any]] = Iterable(Array(123))
v2: Iterable[Iterable[Any]] = List(WrappedArray(123))
З тих пір Int
є підтипом Any
, відбувається об'єднання, і код працює просто чудово.
Якщо ви хочете, щоб ваша функція приймала все, що є підтипом Any
(без допомоги щодо об'єднання Скали) вам доведеться чітко визначити цю поведінку.
Редагувати:
Хоча те, що я кажу, частково правдиве, дивіться відповідь @AlexyRomanov для більш правильної оцінки. Схоже, що "об'єднання" між Array
і Iterable
насправді неявна конверсія, яка викликається при переході Iterable(Array(123))
як параметр (див. ефект цього в моїй декларації від v2
)
Припустимо, у вас є трохи коду, де компілятор очікує типу B
але знаходить тип A
замість цього. Перш ніж видавати помилку, компілятор перевіряє набір неявних функцій перетворення для однієї з типом A => B
. Якщо компілятор знайде задовільну конверсію, перетворення застосовується автоматично (і безшумно).
Причина f3
не подобається v1
це тому, що занадто пізно називати неявне перетворення на внутрішній Array[Int]
і не існує жодної наявної неявної конверсії для Iterable[Array[Int]] => Iterable[Iterable[Int]]
, хоча це було б тривіально, як я показую нижче:
scala> implicit def ItAr2ItIt[T](ItAr: Iterable[Array[T]]): Iterable[Iterable[T]] = ItAr.map(_.toIterable)
ItAr2ItIt: [T](ItAr: Iterable[Array[T]])Iterable[Iterable[T]]
scala> def f3(o:Iterable[Iterable[Any]]):Unit = println("I like what I see!")
f3: (o: Iterable[Iterable[Any]])Unit
scala> val v3 = Iterable(Array(123))
v3: Iterable[Array[Int]] = List(Array(123))
scala> f3(v3)
I like what I see!