После того, как компилятор обработает именованные параметры и параметры по умолчанию, вызовы становятся
foo.copy("foo1.1", foo.name, foo.v1)
и
foo.copy("foo1.1", foo.name, foo.v1, foo.v2)
соответственно. Или, если вы замените параметры типами,
foo.copy[?](String, String, Bar[_])
и
foo.copy[?](String, String, Bar[_], Bar[_])
?
- это параметр типа copy
, который должен быть выведен. В первом случае компилятор в основном говорит: «?
- параметр типа Bar[_]
, даже если я не знаю, что это такое».
Во втором случае параметры типа двух Bar[_]
действительно должно быть таким же, но эта информация теряется к тому времени, когда компилятор делает вывод ?
; они просто Bar[_]
, а не что-то вроде Bar[foo's unknown type parameter]
. Так, например, «?
является параметром типа первого Bar[_]
, даже если я не знаю, что это» не будет работать, потому что, насколько известно компилятору, второй Bar[_]
может быть другим.
Это не ошибка в том смысле, что оно следует спецификации языка; и изменение спецификации, чтобы разрешить это, потребует значительных усилий и усложнит как его, так и компилятор. Возможно, это не лучший компромисс для такого относительно редкого случая.
Другой обходной путь - использовать шаблон переменной типа для временного присвоения имени _
:
foo match { case foo: Foo[a] => foo.copy(id = "foo1.1") }
компилятор теперь видит, что foo.v1
и foo.v2
оба являются Bar[a]
, поэтому результат copy
равен Foo[a]
. После выхода из ветки case
он становится Foo[_]
.